nrf52840dk_lib/
lib.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 development kit (DK).
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//! Pin Configuration
11//! -------------------
12//!
13//! ### `GPIO`
14//!
15//! | #  | Pin   | Ix | Header | Arduino |
16//! |----|-------|----|--------|---------|
17//! | 0  | P1.01 | 33 | P3 1   | D0      |
18//! | 1  | P1.02 | 34 | P3 2   | D1      |
19//! | 2  | P1.03 | 35 | P3 3   | D2      |
20//! | 3  | P1.04 | 36 | P3 4   | D3      |
21//! | 4  | P1.05 | 37 | P3 5   | D4      |
22//! | 5  | P1.06 | 38 | P3 6   | D5      |
23//! | 6  | P1.07 | 39 | P3 7   | D6      |
24//! | 7  | P1.08 | 40 | P3 8   | D7      |
25//! | 8  | P1.10 | 42 | P4 1   | D8      |
26//! | 9  | P1.11 | 43 | P4 2   | D9      |
27//! | 10 | P1.12 | 44 | P4 3   | D10     |
28//! | 11 | P1.13 | 45 | P4 4   | D11     |
29//! | 12 | P1.14 | 46 | P4 5   | D12     |
30//! | 13 | P1.15 | 47 | P4 6   | D13     |
31//! | 14 | P0.26 | 26 | P4 9   | D14     |
32//! | 15 | P0.27 | 27 | P4 10  | D15     |
33//!
34//! ### `GPIO` / Analog Inputs
35//!
36//! | #  | Pin        | Header | Arduino |
37//! |----|------------|--------|---------|
38//! | 16 | P0.03 AIN1 | P2 1   | A0      |
39//! | 17 | P0.04 AIN2 | P2 2   | A1      |
40//! | 18 | P0.28 AIN4 | P2 3   | A2      |
41//! | 19 | P0.29 AIN5 | P2 4   | A3      |
42//! | 20 | P0.30 AIN6 | P2 5   | A4      |
43//! | 21 | P0.31 AIN7 | P2 6   | A5      |
44//! | 22 | P0.02 AIN0 | P4 8   | AVDD    |
45//!
46//! ### Onboard Functions
47//!
48//! | Pin   | Header | Function |
49//! |-------|--------|----------|
50//! | P0.05 | P6 3   | UART RTS |
51//! | P0.06 | P6 4   | UART TXD |
52//! | P0.07 | P6 5   | UART CTS |
53//! | P0.08 | P6 6   | UART RXT |
54//! | P0.11 | P24 1  | Button 1 |
55//! | P0.12 | P24 2  | Button 2 |
56//! | P0.13 | P24 3  | LED 1    |
57//! | P0.14 | P24 4  | LED 2    |
58//! | P0.15 | P24 5  | LED 3    |
59//! | P0.16 | P24 6  | LED 4    |
60//! | P0.18 | P24 8  | Reset    |
61//! | P0.19 | P24 9  | SPI CLK  |
62//! | P0.20 | P24 10 | SPI MOSI |
63//! | P0.21 | P24 11 | SPI MISO |
64//! | P0.22 | P24 12 | SPI CS   |
65//! | P0.24 | P24 14 | Button 3 |
66//! | P0.25 | P24 15 | Button 4 |
67//! | P0.26 | P24 16 | I2C SDA  |
68//! | P0.27 | P24 17 | I2C SCL  |
69
70#![no_std]
71#![deny(missing_docs)]
72
73use core::ptr::addr_of;
74
75use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
76use capsules_extra::net::ieee802154::MacAddress;
77use capsules_extra::net::ipv6::ip_utils::IPAddr;
78use kernel::component::Component;
79use kernel::debug::PanicResources;
80use kernel::hil::led::LedLow;
81use kernel::hil::time::Counter;
82#[allow(unused_imports)]
83use kernel::hil::usb::Client;
84use kernel::platform::{KernelResources, SyscallDriverLookup};
85use kernel::scheduler::round_robin::RoundRobinSched;
86#[allow(unused_imports)]
87use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
88use nrf52840::gpio::Pin;
89use nrf52840::interrupt_service::Nrf52840DefaultPeripherals;
90use nrf52_components::{UartChannel, UartPins};
91
92// The nRF52840DK LEDs (see back of board)
93const LED1_PIN: Pin = Pin::P0_13;
94const LED2_PIN: Pin = Pin::P0_14;
95const LED3_PIN: Pin = Pin::P0_15;
96const LED4_PIN: Pin = Pin::P0_16;
97
98// The nRF52840DK buttons (see back of board)
99const BUTTON1_PIN: Pin = Pin::P0_11;
100const BUTTON2_PIN: Pin = Pin::P0_12;
101const BUTTON3_PIN: Pin = Pin::P0_24;
102const BUTTON4_PIN: Pin = Pin::P0_25;
103const BUTTON_RST_PIN: Pin = Pin::P0_18;
104
105const UART_RTS: Option<Pin> = Some(Pin::P0_05);
106const UART_TXD: Pin = Pin::P0_06;
107const UART_CTS: Option<Pin> = Some(Pin::P0_07);
108const UART_RXD: Pin = Pin::P0_08;
109
110const SPI_MOSI: Pin = Pin::P0_20;
111const SPI_MISO: Pin = Pin::P0_21;
112const SPI_CLK: Pin = Pin::P0_19;
113const SPI_CS: Pin = Pin::P0_22;
114
115const SPI_MX25R6435F_CHIP_SELECT: Pin = Pin::P0_17;
116const SPI_MX25R6435F_WRITE_PROTECT_PIN: Pin = Pin::P0_22;
117const SPI_MX25R6435F_HOLD_PIN: Pin = Pin::P0_23;
118
119/// I2C pins
120const I2C_SDA_PIN: Pin = Pin::P0_26;
121const I2C_SCL_PIN: Pin = Pin::P0_27;
122
123// Constants related to the configuration of the 15.4 network stack
124const PAN_ID: u16 = 0xABCD;
125const DST_MAC_ADDR: capsules_extra::net::ieee802154::MacAddress =
126    capsules_extra::net::ieee802154::MacAddress::Short(49138);
127const DEFAULT_CTX_PREFIX_LEN: u8 = 8; //Length of context for 6LoWPAN compression
128const DEFAULT_CTX_PREFIX: [u8; 16] = [0x0_u8; 16]; //Context for 6LoWPAN Compression
129
130/// Debug Writer
131pub mod io;
132
133// Whether to use UART debugging or Segger RTT (USB) debugging.
134// - Set to false to use UART.
135// - Set to true to use Segger RTT over USB.
136const USB_DEBUGGING: bool = false;
137
138/// This platform's chip type:
139pub type ChipHw = nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>;
140/// Type for the process details printer.
141type ProcessPrinter = capsules_system::process_printer::ProcessPrinterText;
142
143/// Number of concurrent processes this platform supports.
144pub const NUM_PROCS: usize = 8;
145
146use kernel::utilities::single_thread_value::SingleThreadValue;
147
148/// Resources for when a board panics used by io.rs.
149static PANIC_RESOURCES: SingleThreadValue<PanicResources<ChipHw, ProcessPrinter>> =
150    SingleThreadValue::new(PanicResources::new());
151
152kernel::stack_size! {0x2000}
153
154//------------------------------------------------------------------------------
155// SYSCALL DRIVER TYPE DEFINITIONS
156//------------------------------------------------------------------------------
157
158type AlarmDriver = components::alarm::AlarmDriverComponentType<nrf52840::rtc::Rtc<'static>>;
159type RngDriver = components::rng::RngComponentType<nrf52840::trng::Trng<'static>>;
160
161// TicKV
162type Mx25r6435f = components::mx25r6435f::Mx25r6435fComponentType<
163    nrf52840::spi::SPIM<'static>,
164    nrf52840::gpio::GPIOPin<'static>,
165    nrf52840::rtc::Rtc<'static>,
166>;
167const TICKV_PAGE_SIZE: usize =
168    core::mem::size_of::<<Mx25r6435f as kernel::hil::flash::Flash>::Page>();
169type Siphasher24 = components::siphash::Siphasher24ComponentType;
170type TicKVDedicatedFlash =
171    components::tickv::TicKVDedicatedFlashComponentType<Mx25r6435f, Siphasher24, TICKV_PAGE_SIZE>;
172type TicKVKVStore = components::kv::TicKVKVStoreComponentType<
173    TicKVDedicatedFlash,
174    capsules_extra::tickv::TicKVKeyType,
175>;
176type KVStorePermissions = components::kv::KVStorePermissionsComponentType<TicKVKVStore>;
177type VirtualKVPermissions = components::kv::VirtualKVPermissionsComponentType<KVStorePermissions>;
178type KVDriver = components::kv::KVDriverComponentType<VirtualKVPermissions>;
179
180// Temperature
181type TemperatureDriver =
182    components::temperature::TemperatureComponentType<nrf52840::temperature::Temp<'static>>;
183
184// IEEE 802.15.4
185type Ieee802154MacDevice = components::ieee802154::Ieee802154ComponentMacDeviceType<
186    nrf52840::ieee802154_radio::Radio<'static>,
187    nrf52840::aes::AesECB<'static>,
188>;
189/// Userspace 802.15.4 driver with in-kernel packet framing and MAC layer.
190pub type Ieee802154Driver = components::ieee802154::Ieee802154ComponentType<
191    nrf52840::ieee802154_radio::Radio<'static>,
192    nrf52840::aes::AesECB<'static>,
193>;
194
195// EUI64
196/// Userspace EUI64 driver.
197pub type Eui64Driver = components::eui64::Eui64ComponentType;
198
199/// Supported drivers by the platform
200pub struct Platform {
201    ble_radio: &'static capsules_extra::ble_advertising_driver::BLE<
202        'static,
203        nrf52840::ble_radio::Radio<'static>,
204        VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
205    >,
206    button: &'static capsules_core::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
207    pconsole: &'static capsules_core::process_console::ProcessConsole<
208        'static,
209        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
210        VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
211        components::process_console::Capability,
212    >,
213    console: &'static capsules_core::console::Console<'static>,
214    gpio: &'static capsules_core::gpio::GPIO<'static, nrf52840::gpio::GPIOPin<'static>>,
215    led: &'static capsules_core::led::LedDriver<
216        'static,
217        kernel::hil::led::LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
218        4,
219    >,
220    rng: &'static RngDriver,
221    adc: &'static capsules_core::adc::AdcDedicated<'static, nrf52840::adc::Adc<'static>>,
222    temp: &'static TemperatureDriver,
223    /// The IPC driver.
224    pub ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
225    analog_comparator: &'static capsules_extra::analog_comparator::AnalogComparator<
226        'static,
227        nrf52840::acomp::Comparator<'static>,
228    >,
229    alarm: &'static AlarmDriver,
230    i2c_master_slave: &'static capsules_core::i2c_master_slave_driver::I2CMasterSlaveDriver<
231        'static,
232        nrf52840::i2c::TWI<'static>,
233    >,
234    spi_controller: &'static capsules_core::spi_controller::Spi<
235        'static,
236        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
237            'static,
238            nrf52840::spi::SPIM<'static>,
239        >,
240    >,
241    kv_driver: &'static KVDriver,
242    scheduler: &'static RoundRobinSched<'static>,
243    systick: cortexm4::systick::SysTick,
244}
245
246impl SyscallDriverLookup for Platform {
247    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
248    where
249        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
250    {
251        match driver_num {
252            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
253            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
254            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
255            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
256            capsules_core::button::DRIVER_NUM => f(Some(self.button)),
257            capsules_core::rng::DRIVER_NUM => f(Some(self.rng)),
258            capsules_core::adc::DRIVER_NUM => f(Some(self.adc)),
259            capsules_extra::ble_advertising_driver::DRIVER_NUM => f(Some(self.ble_radio)),
260            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temp)),
261            capsules_extra::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
262            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
263            capsules_core::i2c_master_slave_driver::DRIVER_NUM => f(Some(self.i2c_master_slave)),
264            capsules_core::spi_controller::DRIVER_NUM => f(Some(self.spi_controller)),
265            capsules_extra::kv_driver::DRIVER_NUM => f(Some(self.kv_driver)),
266            _ => f(None),
267        }
268    }
269}
270
271impl KernelResources<ChipHw> for Platform {
272    type SyscallDriverLookup = Self;
273    type SyscallFilter = ();
274    type ProcessFault = ();
275    type Scheduler = RoundRobinSched<'static>;
276    type SchedulerTimer = cortexm4::systick::SysTick;
277    type WatchDog = ();
278    type ContextSwitchCallback = ();
279
280    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
281        self
282    }
283    fn syscall_filter(&self) -> &Self::SyscallFilter {
284        &()
285    }
286    fn process_fault(&self) -> &Self::ProcessFault {
287        &()
288    }
289    fn scheduler(&self) -> &Self::Scheduler {
290        self.scheduler
291    }
292    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
293        &self.systick
294    }
295    fn watchdog(&self) -> &Self::WatchDog {
296        &()
297    }
298    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
299        &()
300    }
301}
302
303/// Create the capsules needed for the in-kernel UDP and 15.4 stack.
304pub unsafe fn ieee802154_udp(
305    board_kernel: &'static kernel::Kernel,
306    nrf52840_peripherals: &'static Nrf52840DefaultPeripherals<'static>,
307    mux_alarm: &'static MuxAlarm<nrf52840::rtc::Rtc>,
308) -> (
309    &'static Eui64Driver,
310    &'static Ieee802154Driver,
311    &'static capsules_extra::net::udp::UDPDriver<'static>,
312) {
313    //--------------------------------------------------------------------------
314    // AES
315    //--------------------------------------------------------------------------
316
317    let aes_mux =
318        components::ieee802154::MuxAes128ccmComponent::new(&nrf52840_peripherals.nrf52.ecb)
319            .finalize(components::mux_aes128ccm_component_static!(
320                nrf52840::aes::AesECB
321            ));
322
323    //--------------------------------------------------------------------------
324    // 802.15.4
325    //--------------------------------------------------------------------------
326
327    let device_id = (*addr_of!(nrf52840::ficr::FICR_INSTANCE)).id();
328    let device_id_bottom_16: u16 = u16::from_le_bytes([device_id[0], device_id[1]]);
329
330    let eui64_driver = components::eui64::Eui64Component::new(u64::from_le_bytes(device_id))
331        .finalize(components::eui64_component_static!());
332
333    let (ieee802154_driver, mux_mac) = components::ieee802154::Ieee802154Component::new(
334        board_kernel,
335        capsules_extra::ieee802154::DRIVER_NUM,
336        &nrf52840_peripherals.ieee802154_radio,
337        aes_mux,
338        PAN_ID,
339        device_id_bottom_16,
340        device_id,
341    )
342    .finalize(components::ieee802154_component_static!(
343        nrf52840::ieee802154_radio::Radio,
344        nrf52840::aes::AesECB<'static>
345    ));
346
347    //--------------------------------------------------------------------------
348    // UDP
349    //--------------------------------------------------------------------------
350
351    let local_ip_ifaces = static_init!(
352        [IPAddr; 3],
353        [
354            IPAddr::generate_from_mac(capsules_extra::net::ieee802154::MacAddress::Long(device_id)),
355            IPAddr([
356                0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
357                0x1e, 0x1f,
358            ]),
359            IPAddr::generate_from_mac(capsules_extra::net::ieee802154::MacAddress::Short(
360                device_id_bottom_16
361            )),
362        ]
363    );
364
365    let (udp_send_mux, udp_recv_mux, udp_port_table) = components::udp_mux::UDPMuxComponent::new(
366        mux_mac,
367        DEFAULT_CTX_PREFIX_LEN,
368        DEFAULT_CTX_PREFIX,
369        DST_MAC_ADDR,
370        MacAddress::Long(device_id),
371        local_ip_ifaces,
372        mux_alarm,
373    )
374    .finalize(components::udp_mux_component_static!(
375        nrf52840::rtc::Rtc,
376        Ieee802154MacDevice
377    ));
378
379    // UDP driver initialization happens here
380    let udp_driver = components::udp_driver::UDPDriverComponent::new(
381        board_kernel,
382        capsules_extra::net::udp::driver::DRIVER_NUM,
383        udp_send_mux,
384        udp_recv_mux,
385        udp_port_table,
386        local_ip_ifaces,
387    )
388    .finalize(components::udp_driver_component_static!(nrf52840::rtc::Rtc));
389
390    (eui64_driver, ieee802154_driver, udp_driver)
391}
392
393/// This is in a separate, inline(never) function so that its stack frame is
394/// removed when this function returns. Otherwise, the stack space used for
395/// these static_inits is wasted.
396#[inline(never)]
397pub unsafe fn start_no_pconsole() -> (
398    &'static kernel::Kernel,
399    Platform,
400    &'static ChipHw,
401    &'static Nrf52840DefaultPeripherals<'static>,
402    &'static MuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
403) {
404    //--------------------------------------------------------------------------
405    // INITIAL SETUP
406    //--------------------------------------------------------------------------
407
408    // Apply errata fixes and enable interrupts.
409    nrf52840::init();
410
411    // Initialize deferred calls very early.
412    kernel::deferred_call::initialize_deferred_call_state::<
413        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
414    >();
415
416    // Bind global variables to this thread.
417    PANIC_RESOURCES.bind_to_thread::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>();
418    // Set up peripheral drivers. Called in separate function to reduce stack
419    // usage.
420    let ieee802154_ack_buf = static_init!(
421        [u8; nrf52840::ieee802154_radio::ACK_BUF_SIZE],
422        [0; nrf52840::ieee802154_radio::ACK_BUF_SIZE]
423    );
424    // Initialize chip peripheral drivers
425    let nrf52840_peripherals = static_init!(
426        Nrf52840DefaultPeripherals,
427        Nrf52840DefaultPeripherals::new(ieee802154_ack_buf)
428    );
429
430    // Set up circular peripheral dependencies.
431    nrf52840_peripherals.init();
432    let base_peripherals = &nrf52840_peripherals.nrf52;
433
434    // Configure kernel debug GPIOs as early as possible.
435    let debug_gpios = static_init!(
436        [&'static dyn kernel::hil::gpio::Pin; 3],
437        [
438            &nrf52840_peripherals.gpio_port[LED1_PIN],
439            &nrf52840_peripherals.gpio_port[LED2_PIN],
440            &nrf52840_peripherals.gpio_port[LED3_PIN]
441        ]
442    );
443    kernel::debug::initialize_debug_gpio::<
444        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
445    >();
446    kernel::debug::assign_gpios(debug_gpios);
447
448    // Choose the channel for serial output. This board can be configured to use
449    // either the Segger RTT channel or via UART with traditional TX/RX GPIO
450    // pins.
451    let uart_channel = if USB_DEBUGGING {
452        // Initialize early so any panic beyond this point can use the RTT
453        // memory object.
454        let rtt_memory_refs = components::segger_rtt::SeggerRttMemoryComponent::new()
455            .finalize(components::segger_rtt_memory_component_static!());
456
457        // XXX: This is inherently unsafe as it aliases the mutable reference to
458        // rtt_memory. This aliases reference is only used inside a panic
459        // handler, which should be OK, but maybe we should use a const
460        // reference to rtt_memory and leverage interior mutability instead.
461        self::io::set_rtt_memory(&*core::ptr::from_mut(rtt_memory_refs.rtt_memory));
462
463        UartChannel::Rtt(rtt_memory_refs)
464    } else {
465        UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD))
466    };
467
468    // Create an array to hold process references.
469    let processes = components::process_array::ProcessArrayComponent::new()
470        .finalize(components::process_array_component_static!(NUM_PROCS));
471    PANIC_RESOURCES.get().map(|resources| {
472        resources.processes.put(processes.as_slice());
473    });
474
475    // Setup space to store the core kernel data structure.
476    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
477
478    // Create (and save for panic debugging) a chip object to setup low-level
479    // resources (e.g. MPU, systick).
480    let chip = static_init!(ChipHw, nrf52840::chip::NRF52::new(nrf52840_peripherals));
481    PANIC_RESOURCES.get().map(|resources| {
482        resources.chip.put(chip);
483    });
484
485    // Do nRF configuration and setup. This is shared code with other nRF-based
486    // platforms.
487    nrf52_components::startup::NrfStartupComponent::new(
488        false,
489        BUTTON_RST_PIN,
490        nrf52840::uicr::Regulator0Output::DEFAULT,
491        &base_peripherals.nvmc,
492    )
493    .finalize(());
494
495    //--------------------------------------------------------------------------
496    // CAPABILITIES
497    //--------------------------------------------------------------------------
498
499    // Create capabilities that the board needs to call certain protected kernel
500    // functions.
501    let memory_allocation_capability = create_capability!(capabilities::MemoryAllocationCapability);
502    let gpio_port = &nrf52840_peripherals.gpio_port;
503
504    //--------------------------------------------------------------------------
505    // GPIO
506    //--------------------------------------------------------------------------
507
508    // Expose the D0-D13 Arduino GPIO pins to userspace.
509    let gpio = components::gpio::GpioComponent::new(
510        board_kernel,
511        capsules_core::gpio::DRIVER_NUM,
512        components::gpio_component_helper!(
513            nrf52840::gpio::GPIOPin,
514            0 => &nrf52840_peripherals.gpio_port[Pin::P1_01],
515            1 => &nrf52840_peripherals.gpio_port[Pin::P1_02],
516            2 => &nrf52840_peripherals.gpio_port[Pin::P1_03],
517            3 => &nrf52840_peripherals.gpio_port[Pin::P1_04],
518            4 => &nrf52840_peripherals.gpio_port[Pin::P1_05],
519            5 => &nrf52840_peripherals.gpio_port[Pin::P1_06],
520            6 => &nrf52840_peripherals.gpio_port[Pin::P1_07],
521            7 => &nrf52840_peripherals.gpio_port[Pin::P1_08],
522            // Avoid exposing the I2C pins to userspace, as these are used in
523            // some tutorials (e.g., `nrf52840dk-thread-tutorial`).
524            //
525            // In the future we might want to make this configurable.
526            //
527            // 8 => &nrf52840_peripherals.gpio_port[Pin::P1_10],
528            // 9 => &nrf52840_peripherals.gpio_port[Pin::P1_11],
529            10 => &nrf52840_peripherals.gpio_port[Pin::P1_12],
530            11 => &nrf52840_peripherals.gpio_port[Pin::P1_13],
531            12 => &nrf52840_peripherals.gpio_port[Pin::P1_14],
532            13 => &nrf52840_peripherals.gpio_port[Pin::P1_15],
533        ),
534    )
535    .finalize(components::gpio_component_static!(nrf52840::gpio::GPIOPin));
536
537    //--------------------------------------------------------------------------
538    // BUTTONS
539    //--------------------------------------------------------------------------
540
541    let button = components::button::ButtonComponent::new(
542        board_kernel,
543        capsules_core::button::DRIVER_NUM,
544        components::button_component_helper!(
545            nrf52840::gpio::GPIOPin,
546            (
547                &nrf52840_peripherals.gpio_port[BUTTON1_PIN],
548                kernel::hil::gpio::ActivationMode::ActiveLow,
549                kernel::hil::gpio::FloatingState::PullUp
550            ),
551            (
552                &nrf52840_peripherals.gpio_port[BUTTON2_PIN],
553                kernel::hil::gpio::ActivationMode::ActiveLow,
554                kernel::hil::gpio::FloatingState::PullUp
555            ),
556            (
557                &nrf52840_peripherals.gpio_port[BUTTON3_PIN],
558                kernel::hil::gpio::ActivationMode::ActiveLow,
559                kernel::hil::gpio::FloatingState::PullUp
560            ),
561            (
562                &nrf52840_peripherals.gpio_port[BUTTON4_PIN],
563                kernel::hil::gpio::ActivationMode::ActiveLow,
564                kernel::hil::gpio::FloatingState::PullUp
565            )
566        ),
567    )
568    .finalize(components::button_component_static!(
569        nrf52840::gpio::GPIOPin
570    ));
571
572    //--------------------------------------------------------------------------
573    // LEDs
574    //--------------------------------------------------------------------------
575
576    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
577        LedLow<'static, nrf52840::gpio::GPIOPin>,
578        LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
579        LedLow::new(&nrf52840_peripherals.gpio_port[LED2_PIN]),
580        LedLow::new(&nrf52840_peripherals.gpio_port[LED3_PIN]),
581        LedLow::new(&nrf52840_peripherals.gpio_port[LED4_PIN]),
582    ));
583
584    //--------------------------------------------------------------------------
585    // TIMER
586    //--------------------------------------------------------------------------
587
588    let rtc = &base_peripherals.rtc;
589    let _ = rtc.start();
590    let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
591        .finalize(components::alarm_mux_component_static!(nrf52840::rtc::Rtc));
592    let alarm = components::alarm::AlarmDriverComponent::new(
593        board_kernel,
594        capsules_core::alarm::DRIVER_NUM,
595        mux_alarm,
596    )
597    .finalize(components::alarm_component_static!(nrf52840::rtc::Rtc));
598
599    //--------------------------------------------------------------------------
600    // UART & CONSOLE & DEBUG
601    //--------------------------------------------------------------------------
602
603    let uart_channel = nrf52_components::UartChannelComponent::new(
604        uart_channel,
605        mux_alarm,
606        &base_peripherals.uarte0,
607    )
608    .finalize(nrf52_components::uart_channel_component_static!(
609        nrf52840::rtc::Rtc
610    ));
611
612    // Tool for displaying information about processes.
613    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
614        .finalize(components::process_printer_text_component_static!());
615    PANIC_RESOURCES.get().map(|resources| {
616        resources.printer.put(process_printer);
617    });
618
619    // Virtualize the UART channel for the console and for kernel debug.
620    let uart_mux = components::console::UartMuxComponent::new(uart_channel, 115200)
621        .finalize(components::uart_mux_component_static!());
622
623    // Create the process console, an interactive terminal for managing
624    // processes.
625    let pconsole = components::process_console::ProcessConsoleComponent::new(
626        board_kernel,
627        uart_mux,
628        mux_alarm,
629        process_printer,
630        Some(cortexm4::support::reset),
631    )
632    .finalize(components::process_console_component_static!(
633        nrf52840::rtc::Rtc<'static>
634    ));
635
636    // Setup the serial console for userspace.
637    let console = components::console::ConsoleComponent::new(
638        board_kernel,
639        capsules_core::console::DRIVER_NUM,
640        uart_mux,
641    )
642    .finalize(components::console_component_static!());
643
644    // Create the debugger object that handles calls to `debug!()`.
645    components::debug_writer::DebugWriterComponent::new::<
646        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
647    >(
648        uart_mux,
649        create_capability!(capabilities::SetDebugWriterCapability),
650    )
651    .finalize(components::debug_writer_component_static!());
652
653    //--------------------------------------------------------------------------
654    // BLE
655    //--------------------------------------------------------------------------
656
657    let ble_radio = components::ble::BLEComponent::new(
658        board_kernel,
659        capsules_extra::ble_advertising_driver::DRIVER_NUM,
660        &base_peripherals.ble_radio,
661        mux_alarm,
662    )
663    .finalize(components::ble_component_static!(
664        nrf52840::rtc::Rtc,
665        nrf52840::ble_radio::Radio
666    ));
667
668    //--------------------------------------------------------------------------
669    // TEMPERATURE (internal)
670    //--------------------------------------------------------------------------
671
672    let temp = components::temperature::TemperatureComponent::new(
673        board_kernel,
674        capsules_extra::temperature::DRIVER_NUM,
675        &base_peripherals.temp,
676    )
677    .finalize(components::temperature_component_static!(
678        nrf52840::temperature::Temp
679    ));
680
681    //--------------------------------------------------------------------------
682    // RANDOM NUMBER GENERATOR
683    //--------------------------------------------------------------------------
684
685    let rng = components::rng::RngComponent::new(
686        board_kernel,
687        capsules_core::rng::DRIVER_NUM,
688        &base_peripherals.trng,
689    )
690    .finalize(components::rng_component_static!(nrf52840::trng::Trng));
691
692    //--------------------------------------------------------------------------
693    // ADC
694    //--------------------------------------------------------------------------
695
696    let adc_channels = static_init!(
697        [nrf52840::adc::AdcChannelSetup; 6],
698        [
699            nrf52840::adc::AdcChannelSetup::new(nrf52840::adc::AdcChannel::AnalogInput1),
700            nrf52840::adc::AdcChannelSetup::new(nrf52840::adc::AdcChannel::AnalogInput2),
701            nrf52840::adc::AdcChannelSetup::new(nrf52840::adc::AdcChannel::AnalogInput4),
702            nrf52840::adc::AdcChannelSetup::new(nrf52840::adc::AdcChannel::AnalogInput5),
703            nrf52840::adc::AdcChannelSetup::new(nrf52840::adc::AdcChannel::AnalogInput6),
704            nrf52840::adc::AdcChannelSetup::new(nrf52840::adc::AdcChannel::AnalogInput7),
705        ]
706    );
707    let adc = components::adc::AdcDedicatedComponent::new(
708        &base_peripherals.adc,
709        adc_channels,
710        board_kernel,
711        capsules_core::adc::DRIVER_NUM,
712    )
713    .finalize(components::adc_dedicated_component_static!(
714        nrf52840::adc::Adc
715    ));
716
717    //--------------------------------------------------------------------------
718    // SPI
719    //--------------------------------------------------------------------------
720
721    let mux_spi = components::spi::SpiMuxComponent::new(&base_peripherals.spim0)
722        .finalize(components::spi_mux_component_static!(nrf52840::spi::SPIM));
723
724    // Create the SPI system call capsule.
725    let spi_controller = components::spi::SpiSyscallComponent::new(
726        board_kernel,
727        mux_spi,
728        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
729            &gpio_port[SPI_CS],
730        ),
731        capsules_core::spi_controller::DRIVER_NUM,
732    )
733    .finalize(components::spi_syscall_component_static!(
734        nrf52840::spi::SPIM
735    ));
736
737    base_peripherals.spim0.configure(
738        nrf52840::pinmux::Pinmux::new(SPI_MOSI as u32),
739        nrf52840::pinmux::Pinmux::new(SPI_MISO as u32),
740        nrf52840::pinmux::Pinmux::new(SPI_CLK as u32),
741    );
742
743    //--------------------------------------------------------------------------
744    // ONBOARD EXTERNAL FLASH
745    //--------------------------------------------------------------------------
746
747    let mx25r6435f = components::mx25r6435f::Mx25r6435fComponent::new(
748        Some(&gpio_port[SPI_MX25R6435F_WRITE_PROTECT_PIN]),
749        Some(&gpio_port[SPI_MX25R6435F_HOLD_PIN]),
750        &gpio_port[SPI_MX25R6435F_CHIP_SELECT],
751        mux_alarm,
752        mux_spi,
753    )
754    .finalize(components::mx25r6435f_component_static!(
755        nrf52840::spi::SPIM,
756        nrf52840::gpio::GPIOPin,
757        nrf52840::rtc::Rtc
758    ));
759
760    //--------------------------------------------------------------------------
761    // TICKV
762    //--------------------------------------------------------------------------
763
764    // Static buffer to use when reading/writing flash for TicKV.
765    let page_buffer = static_init!(
766        <Mx25r6435f as kernel::hil::flash::Flash>::Page,
767        <Mx25r6435f as kernel::hil::flash::Flash>::Page::default()
768    );
769
770    // SipHash for creating TicKV hashed keys.
771    let sip_hash = components::siphash::Siphasher24Component::new()
772        .finalize(components::siphasher24_component_static!());
773
774    // TicKV with Tock wrapper/interface.
775    let tickv = components::tickv::TicKVDedicatedFlashComponent::new(
776        sip_hash,
777        mx25r6435f,
778        0, // start at the beginning of the flash chip
779        (capsules_extra::mx25r6435f::SECTOR_SIZE as usize) * 32, // arbitrary size of 32 pages
780        page_buffer,
781    )
782    .finalize(components::tickv_dedicated_flash_component_static!(
783        Mx25r6435f,
784        Siphasher24,
785        TICKV_PAGE_SIZE,
786    ));
787
788    // KVSystem interface to KV (built on TicKV).
789    let tickv_kv_store = components::kv::TicKVKVStoreComponent::new(tickv).finalize(
790        components::tickv_kv_store_component_static!(
791            TicKVDedicatedFlash,
792            capsules_extra::tickv::TicKVKeyType,
793        ),
794    );
795
796    let kv_store_permissions = components::kv::KVStorePermissionsComponent::new(tickv_kv_store)
797        .finalize(components::kv_store_permissions_component_static!(
798            TicKVKVStore
799        ));
800
801    // Share the KV stack with a mux.
802    let mux_kv = components::kv::KVPermissionsMuxComponent::new(kv_store_permissions).finalize(
803        components::kv_permissions_mux_component_static!(KVStorePermissions),
804    );
805
806    // Create a virtual component for the userspace driver.
807    let virtual_kv_driver = components::kv::VirtualKVPermissionsComponent::new(mux_kv).finalize(
808        components::virtual_kv_permissions_component_static!(KVStorePermissions),
809    );
810
811    // Userspace driver for KV.
812    let kv_driver = components::kv::KVDriverComponent::new(
813        virtual_kv_driver,
814        board_kernel,
815        capsules_extra::kv_driver::DRIVER_NUM,
816    )
817    .finalize(components::kv_driver_component_static!(
818        VirtualKVPermissions
819    ));
820
821    //--------------------------------------------------------------------------
822    // I2C CONTROLLER/TARGET
823    //--------------------------------------------------------------------------
824
825    let i2c_master_slave = components::i2c::I2CMasterSlaveDriverComponent::new(
826        board_kernel,
827        capsules_core::i2c_master_slave_driver::DRIVER_NUM,
828        &base_peripherals.twi1,
829    )
830    .finalize(components::i2c_master_slave_component_static!(
831        nrf52840::i2c::TWI
832    ));
833
834    base_peripherals.twi1.configure(
835        nrf52840::pinmux::Pinmux::new(I2C_SCL_PIN as u32),
836        nrf52840::pinmux::Pinmux::new(I2C_SDA_PIN as u32),
837    );
838    base_peripherals.twi1.set_speed(nrf52840::i2c::Speed::K400);
839
840    //--------------------------------------------------------------------------
841    // ANALOG COMPARATOR
842    //--------------------------------------------------------------------------
843
844    // Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
845    // These are hardcoded pin assignments specified in the driver
846    let analog_comparator_channel = static_init!(
847        nrf52840::acomp::Channel,
848        nrf52840::acomp::Channel::new(nrf52840::acomp::ChannelNumber::AC0)
849    );
850    let analog_comparator = components::analog_comparator::AnalogComparatorComponent::new(
851        &base_peripherals.acomp,
852        components::analog_comparator_component_helper!(
853            nrf52840::acomp::Channel,
854            analog_comparator_channel
855        ),
856        board_kernel,
857        capsules_extra::analog_comparator::DRIVER_NUM,
858    )
859    .finalize(components::analog_comparator_component_static!(
860        nrf52840::acomp::Comparator
861    ));
862
863    //--------------------------------------------------------------------------
864    // NRF CLOCK SETUP
865    //--------------------------------------------------------------------------
866
867    nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
868
869    //--------------------------------------------------------------------------
870    // USB EXAMPLES
871    //--------------------------------------------------------------------------
872    // Uncomment to experiment with this.
873
874    // // Create the strings we include in the USB descriptor.
875    // let strings = static_init!(
876    //     [&str; 3],
877    //     [
878    //         "Nordic Semiconductor", // Manufacturer
879    //         "nRF52840dk - TockOS",  // Product
880    //         "serial0001",           // Serial number
881    //     ]
882    // );
883
884    // CTAP Example
885    //
886    // let (ctap, _ctap_driver) = components::ctap::CtapComponent::new(
887    //     board_kernel,
888    //     capsules_extra::ctap::DRIVER_NUM,
889    //     &nrf52840_peripherals.usbd,
890    //     0x1915, // Nordic Semiconductor
891    //     0x503a, // lowRISC generic FS USB
892    //     strings,
893    // )
894    // .finalize(components::ctap_component_static!(nrf52840::usbd::Usbd));
895
896    // ctap.enable();
897    // ctap.attach();
898
899    // // Keyboard HID Example
900    // type UsbHw = nrf52840::usbd::Usbd<'static>;
901    // let usb_device = &nrf52840_peripherals.usbd;
902
903    // let (keyboard_hid, keyboard_hid_driver) = components::keyboard_hid::KeyboardHidComponent::new(
904    //     board_kernel,
905    //     capsules_core::driver::NUM::KeyboardHid as usize,
906    //     usb_device,
907    //     0x1915, // Nordic Semiconductor
908    //     0x503a,
909    //     strings,
910    // )
911    // .finalize(components::keyboard_hid_component_static!(UsbHw));
912
913    // keyboard_hid.enable();
914    // keyboard_hid.attach();
915
916    //--------------------------------------------------------------------------
917    // PLATFORM SETUP, SCHEDULER, AND START KERNEL LOOP
918    //--------------------------------------------------------------------------
919
920    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
921        .finalize(components::round_robin_component_static!(NUM_PROCS));
922
923    let platform = Platform {
924        button,
925        ble_radio,
926        pconsole,
927        console,
928        led,
929        gpio,
930        rng,
931        adc,
932        temp,
933        alarm,
934        analog_comparator,
935        ipc: kernel::ipc::IPC::new(
936            board_kernel,
937            kernel::ipc::DRIVER_NUM,
938            &memory_allocation_capability,
939        ),
940        i2c_master_slave,
941        spi_controller,
942        kv_driver,
943        scheduler,
944        systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
945    };
946
947    base_peripherals.adc.calibrate();
948
949    debug!("Initialization complete. Entering main loop\r");
950    debug!("{}", &*addr_of!(nrf52840::ficr::FICR_INSTANCE));
951
952    (
953        board_kernel,
954        platform,
955        chip,
956        nrf52840_peripherals,
957        mux_alarm,
958    )
959}
960
961/// This is in a separate, inline(never) function so that its stack frame is
962/// removed when this function returns. Otherwise, the stack space used for
963/// these static_inits is wasted.
964#[inline(never)]
965pub unsafe fn start() -> (
966    &'static kernel::Kernel,
967    Platform,
968    &'static ChipHw,
969    &'static Nrf52840DefaultPeripherals<'static>,
970    &'static MuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
971) {
972    let (kernel, platform, chip, peripherals, mux_alarm) = start_no_pconsole();
973    let _ = platform.pconsole.start();
974    (kernel, platform, chip, peripherals, mux_alarm)
975}