redboard_artemis_nano/
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//! Board file for SparkFun Redboard Artemis Nano
6//!
7//! - <https://www.sparkfun.com/products/15443>
8
9#![no_std]
10#![no_main]
11#![deny(missing_docs)]
12#![feature(custom_test_frameworks)]
13#![test_runner(test_runner)]
14#![reexport_test_harness_main = "test_main"]
15
16use apollo3::chip::Apollo3DefaultPeripherals;
17use capsules_core::virtualizers::virtual_alarm::MuxAlarm;
18use capsules_core::virtualizers::virtual_alarm::VirtualMuxAlarm;
19use components::bme280::Bme280Component;
20use components::ccs811::Ccs811Component;
21use kernel::capabilities;
22use kernel::component::Component;
23use kernel::debug::PanicResources;
24use kernel::hil::i2c::I2CMaster;
25use kernel::hil::led::LedHigh;
26use kernel::hil::time::Counter;
27use kernel::platform::{KernelResources, SyscallDriverLookup};
28use kernel::scheduler::round_robin::RoundRobinSched;
29use kernel::utilities::single_thread_value::SingleThreadValue;
30use kernel::{create_capability, debug, static_init};
31
32/// Support routines for debugging I/O.
33pub mod io;
34
35#[cfg(test)]
36mod tests;
37
38// Number of concurrent processes this platform supports.
39const NUM_PROCS: usize = 4;
40
41type ChipHw = apollo3::chip::Apollo3<Apollo3DefaultPeripherals>;
42type ProcessPrinterInUse = capsules_system::process_printer::ProcessPrinterText;
43
44/// Resources for when a board panics used by io.rs.
45static PANIC_RESOURCES: SingleThreadValue<PanicResources<ChipHw, ProcessPrinterInUse>> =
46    SingleThreadValue::new(PanicResources::new());
47
48// How should the kernel respond when a process faults.
49const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
50    capsules_system::process_policies::PanicFaultPolicy {};
51
52// Test access to the peripherals
53#[cfg(test)]
54static mut PERIPHERALS: Option<&'static Apollo3DefaultPeripherals> = None;
55// Test access to board
56#[cfg(test)]
57static mut BOARD: Option<&'static kernel::Kernel> = None;
58// Test access to platform
59#[cfg(test)]
60static mut PLATFORM: Option<&'static RedboardArtemisNano> = None;
61// Test access to main loop capability
62#[cfg(test)]
63static mut MAIN_CAP: Option<&dyn kernel::capabilities::MainLoopCapability> = None;
64// Test access to alarm
65static mut ALARM: Option<&'static MuxAlarm<'static, apollo3::stimer::STimer<'static>>> = None;
66// Test access to sensors
67static mut BME280: Option<
68    &'static capsules_extra::bme280::Bme280<
69        'static,
70        capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
71    >,
72> = None;
73static mut CCS811: Option<&'static capsules_extra::ccs811::Ccs811<'static>> = None;
74
75kernel::stack_size! {0x1000}
76
77type BME280Sensor = components::bme280::Bme280ComponentType<
78    capsules_core::virtualizers::virtual_i2c::I2CDevice<'static, apollo3::iom::Iom<'static>>,
79>;
80type TemperatureDriver = components::temperature::TemperatureComponentType<BME280Sensor>;
81type HumidityDriver = components::humidity::HumidityComponentType<BME280Sensor>;
82
83/// A structure representing this platform that holds references to all
84/// capsules for this platform.
85struct RedboardArtemisNano {
86    alarm: &'static capsules_core::alarm::AlarmDriver<
87        'static,
88        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
89    >,
90    led: &'static capsules_core::led::LedDriver<
91        'static,
92        LedHigh<'static, apollo3::gpio::GpioPin<'static>>,
93        1,
94    >,
95    gpio: &'static capsules_core::gpio::GPIO<'static, apollo3::gpio::GpioPin<'static>>,
96    console: &'static capsules_core::console::Console<'static>,
97    i2c_master:
98        &'static capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
99    spi_controller: &'static capsules_core::spi_controller::Spi<
100        'static,
101        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
102            'static,
103            apollo3::iom::Iom<'static>,
104        >,
105    >,
106    ble_radio: &'static capsules_extra::ble_advertising_driver::BLE<
107        'static,
108        apollo3::ble::Ble<'static>,
109        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
110    >,
111    temperature: &'static TemperatureDriver,
112    humidity: &'static HumidityDriver,
113    air_quality: &'static capsules_extra::air_quality::AirQualitySensor<'static>,
114    scheduler: &'static RoundRobinSched<'static>,
115    systick: cortexm4::systick::SysTick,
116}
117
118/// Mapping of integer syscalls to objects that implement syscalls.
119impl SyscallDriverLookup for RedboardArtemisNano {
120    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
121    where
122        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
123    {
124        match driver_num {
125            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
126            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
127            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
128            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
129            capsules_core::i2c_master::DRIVER_NUM => f(Some(self.i2c_master)),
130            capsules_core::spi_controller::DRIVER_NUM => f(Some(self.spi_controller)),
131            capsules_extra::ble_advertising_driver::DRIVER_NUM => f(Some(self.ble_radio)),
132            capsules_extra::temperature::DRIVER_NUM => f(Some(self.temperature)),
133            capsules_extra::humidity::DRIVER_NUM => f(Some(self.humidity)),
134            capsules_extra::air_quality::DRIVER_NUM => f(Some(self.air_quality)),
135            _ => f(None),
136        }
137    }
138}
139
140impl KernelResources<apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> for RedboardArtemisNano {
141    type SyscallDriverLookup = Self;
142    type SyscallFilter = ();
143    type ProcessFault = ();
144    type Scheduler = RoundRobinSched<'static>;
145    type SchedulerTimer = cortexm4::systick::SysTick;
146    type WatchDog = ();
147    type ContextSwitchCallback = ();
148
149    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
150        self
151    }
152    fn syscall_filter(&self) -> &Self::SyscallFilter {
153        &()
154    }
155    fn process_fault(&self) -> &Self::ProcessFault {
156        &()
157    }
158    fn scheduler(&self) -> &Self::Scheduler {
159        self.scheduler
160    }
161    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
162        &self.systick
163    }
164    fn watchdog(&self) -> &Self::WatchDog {
165        &()
166    }
167    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
168        &()
169    }
170}
171
172// Ensure that `setup()` is never inlined
173// This helps reduce the stack frame, see https://github.com/tock/tock/issues/3518
174#[inline(never)]
175unsafe fn setup() -> (
176    &'static kernel::Kernel,
177    &'static RedboardArtemisNano,
178    &'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
179    &'static Apollo3DefaultPeripherals,
180) {
181    // Initialize deferred calls very early.
182    kernel::deferred_call::initialize_deferred_call_state::<
183        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
184    >();
185
186    // Bind global variables to this thread.
187    PANIC_RESOURCES.bind_to_thread::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>();
188
189    let peripherals = static_init!(Apollo3DefaultPeripherals, Apollo3DefaultPeripherals::new());
190
191    // No need to statically allocate mcu/pwr/clk_ctrl because they are only used in main!
192    let mcu_ctrl = apollo3::mcuctrl::McuCtrl::new();
193    let pwr_ctrl = apollo3::pwrctrl::PwrCtrl::new();
194    let clkgen = apollo3::clkgen::ClkGen::new();
195
196    clkgen.set_clock_frequency(apollo3::clkgen::ClockFrequency::Freq48MHz);
197
198    // initialize capabilities
199    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
200    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
201
202    // Create an array to hold process references.
203    let processes = components::process_array::ProcessArrayComponent::new()
204        .finalize(components::process_array_component_static!(NUM_PROCS));
205    PANIC_RESOURCES.get().map(|resources| {
206        resources.processes.put(processes.as_slice());
207    });
208
209    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
210
211    // Power up components
212    pwr_ctrl.enable_uart0();
213    pwr_ctrl.enable_iom0();
214    pwr_ctrl.enable_iom2();
215    pwr_ctrl.enable_ios();
216
217    peripherals.init();
218
219    // Enable PinCfg
220    peripherals
221        .gpio_port
222        .enable_uart(&peripherals.gpio_port[48], &peripherals.gpio_port[49]);
223    // Enable SDA and SCL for I2C2 (exposed via Qwiic)
224    peripherals
225        .gpio_port
226        .enable_i2c(&peripherals.gpio_port[25], &peripherals.gpio_port[27]);
227    // Enable Main SPI
228    peripherals.gpio_port.enable_spi(
229        &peripherals.gpio_port[5],
230        &peripherals.gpio_port[7],
231        &peripherals.gpio_port[6],
232    );
233    // Enable I2C slave device
234    peripherals
235        .gpio_port
236        .enable_i2c_slave(&peripherals.gpio_port[1], &peripherals.gpio_port[0]);
237
238    // Configure kernel debug gpios as early as possible
239    let debug_gpios = static_init!(
240        [&'static dyn kernel::hil::gpio::Pin; 1],
241        [
242            // Blue LED
243            &peripherals.gpio_port[19]
244        ]
245    );
246    kernel::debug::initialize_debug_gpio::<
247        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
248    >();
249    kernel::debug::assign_gpios(debug_gpios);
250
251    // Create a shared UART channel for the console and for kernel debug.
252    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
253        .finalize(components::uart_mux_component_static!());
254
255    // Setup the console.
256    let console = components::console::ConsoleComponent::new(
257        board_kernel,
258        capsules_core::console::DRIVER_NUM,
259        uart_mux,
260    )
261    .finalize(components::console_component_static!());
262    // Create the debugger object that handles calls to `debug!()`.
263    components::debug_writer::DebugWriterComponent::new::<
264        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
265    >(
266        uart_mux,
267        create_capability!(capabilities::SetDebugWriterCapability),
268    )
269    .finalize(components::debug_writer_component_static!());
270
271    // LEDs
272    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
273        LedHigh<'static, apollo3::gpio::GpioPin>,
274        LedHigh::new(&peripherals.gpio_port[19]),
275    ));
276
277    // GPIOs
278    // These are also ADC channels, but let's expose them as GPIOs
279    let gpio = components::gpio::GpioComponent::new(
280        board_kernel,
281        capsules_core::gpio::DRIVER_NUM,
282        components::gpio_component_helper!(
283            apollo3::gpio::GpioPin,
284            0 => &peripherals.gpio_port[13],  // A0
285            1 => &peripherals.gpio_port[33],  // A1
286            2 => &peripherals.gpio_port[11],  // A2
287            3 => &peripherals.gpio_port[29],  // A3
288            5 => &peripherals.gpio_port[31]  // A5
289        ),
290    )
291    .finalize(components::gpio_component_static!(apollo3::gpio::GpioPin));
292
293    // Create a shared virtualisation mux layer on top of a single hardware
294    // alarm.
295    let _ = peripherals.stimer.start();
296    let mux_alarm = components::alarm::AlarmMuxComponent::new(&peripherals.stimer).finalize(
297        components::alarm_mux_component_static!(apollo3::stimer::STimer),
298    );
299    let alarm = components::alarm::AlarmDriverComponent::new(
300        board_kernel,
301        capsules_core::alarm::DRIVER_NUM,
302        mux_alarm,
303    )
304    .finalize(components::alarm_component_static!(apollo3::stimer::STimer));
305    ALARM = Some(mux_alarm);
306
307    // Create a process printer for panic.
308    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
309        .finalize(components::process_printer_text_component_static!());
310    PANIC_RESOURCES.get().map(|resources| {
311        resources.printer.put(process_printer);
312    });
313
314    // Init the I2C device attached via Qwiic
315    let i2c_master_buffer = static_init!(
316        [u8; capsules_core::i2c_master::BUFFER_LENGTH],
317        [0; capsules_core::i2c_master::BUFFER_LENGTH]
318    );
319    let i2c_master = static_init!(
320        capsules_core::i2c_master::I2CMasterDriver<'static, apollo3::iom::Iom<'static>>,
321        capsules_core::i2c_master::I2CMasterDriver::new(
322            &peripherals.iom2,
323            i2c_master_buffer,
324            board_kernel.create_grant(
325                capsules_core::i2c_master::DRIVER_NUM,
326                &memory_allocation_cap
327            )
328        )
329    );
330
331    peripherals.iom2.set_master_client(i2c_master);
332    peripherals.iom2.enable();
333
334    let mux_i2c = components::i2c::I2CMuxComponent::new(&peripherals.iom2, None)
335        .finalize(components::i2c_mux_component_static!(apollo3::iom::Iom));
336
337    let bme280 = Bme280Component::new(mux_i2c, 0x77)
338        .finalize(components::bme280_component_static!(apollo3::iom::Iom));
339    let temperature = components::temperature::TemperatureComponent::new(
340        board_kernel,
341        capsules_extra::temperature::DRIVER_NUM,
342        bme280,
343    )
344    .finalize(components::temperature_component_static!(BME280Sensor));
345    let humidity = components::humidity::HumidityComponent::new(
346        board_kernel,
347        capsules_extra::humidity::DRIVER_NUM,
348        bme280,
349    )
350    .finalize(components::humidity_component_static!(BME280Sensor));
351    BME280 = Some(bme280);
352
353    let ccs811 = Ccs811Component::new(mux_i2c, 0x5B)
354        .finalize(components::ccs811_component_static!(apollo3::iom::Iom));
355    let air_quality = components::air_quality::AirQualityComponent::new(
356        board_kernel,
357        capsules_extra::temperature::DRIVER_NUM,
358        ccs811,
359    )
360    .finalize(components::air_quality_component_static!());
361    CCS811 = Some(ccs811);
362
363    // Init the SPI controller
364    let mux_spi = components::spi::SpiMuxComponent::new(&peripherals.iom0).finalize(
365        components::spi_mux_component_static!(apollo3::iom::Iom<'static>),
366    );
367
368    // The IOM0 expects an auto chip select on pin D11 or D15, neither of
369    // which are broken out on the board. So the CS control must be manual
370    let spi_controller = components::spi::SpiSyscallComponent::new(
371        board_kernel,
372        mux_spi,
373        kernel::hil::spi::cs::IntoChipSelect::<_, kernel::hil::spi::cs::ActiveLow>::into_cs(
374            &peripherals.gpio_port[35], // A14
375        ),
376        capsules_core::spi_controller::DRIVER_NUM,
377    )
378    .finalize(components::spi_syscall_component_static!(
379        apollo3::iom::Iom<'static>
380    ));
381
382    // Setup BLE
383    mcu_ctrl.enable_ble();
384    clkgen.enable_ble();
385    pwr_ctrl.enable_ble();
386    peripherals.ble.setup_clocks();
387    mcu_ctrl.reset_ble();
388    peripherals.ble.power_up();
389    peripherals.ble.ble_initialise();
390
391    let ble_radio = components::ble::BLEComponent::new(
392        board_kernel,
393        capsules_extra::ble_advertising_driver::DRIVER_NUM,
394        &peripherals.ble,
395        mux_alarm,
396    )
397    .finalize(components::ble_component_static!(
398        apollo3::stimer::STimer,
399        apollo3::ble::Ble,
400    ));
401
402    mcu_ctrl.print_chip_revision();
403
404    debug!("Initialization complete. Entering main loop");
405
406    // These symbols are defined in the linker script.
407    extern "C" {
408        /// Beginning of the ROM region containing app images.
409        static _sapps: u8;
410        /// End of the ROM region containing app images.
411        static _eapps: u8;
412        /// Beginning of the RAM region for app memory.
413        static mut _sappmem: u8;
414        /// End of the RAM region for app memory.
415        static _eappmem: u8;
416    }
417
418    let scheduler = components::sched::round_robin::RoundRobinComponent::new(processes)
419        .finalize(components::round_robin_component_static!(NUM_PROCS));
420
421    let systick = cortexm4::systick::SysTick::new_with_calibration(48_000_000);
422
423    let artemis_nano = static_init!(
424        RedboardArtemisNano,
425        RedboardArtemisNano {
426            alarm,
427            led,
428            gpio,
429            console,
430            i2c_master,
431            spi_controller,
432            ble_radio,
433            temperature,
434            humidity,
435            air_quality,
436            scheduler,
437            systick,
438        }
439    );
440
441    let chip = static_init!(
442        apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
443        apollo3::chip::Apollo3::new(peripherals)
444    );
445    PANIC_RESOURCES.get().map(|resources| {
446        resources.chip.put(chip);
447    });
448
449    kernel::process::load_processes(
450        board_kernel,
451        chip,
452        core::slice::from_raw_parts(
453            core::ptr::addr_of!(_sapps),
454            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
455        ),
456        core::slice::from_raw_parts_mut(
457            core::ptr::addr_of_mut!(_sappmem),
458            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
459        ),
460        &FAULT_RESPONSE,
461        &process_mgmt_cap,
462    )
463    .unwrap_or_else(|err| {
464        debug!("Error loading processes!");
465        debug!("{:?}", err);
466    });
467
468    (board_kernel, artemis_nano, chip, peripherals)
469}
470
471/// Main function.
472///
473/// This function is called from the arch crate after some very basic RISC-V
474/// setup and RAM initialization.
475#[no_mangle]
476pub unsafe fn main() {
477    apollo3::init();
478
479    #[cfg(test)]
480    test_main();
481
482    #[cfg(not(test))]
483    {
484        let (board_kernel, esp32_c3_board, chip, _peripherals) = setup();
485
486        let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
487
488        board_kernel.kernel_loop(
489            esp32_c3_board,
490            chip,
491            None::<&kernel::ipc::IPC<{ NUM_PROCS as u8 }>>,
492            &main_loop_cap,
493        );
494    }
495}
496
497#[cfg(test)]
498use kernel::platform::watchdog::WatchDog;
499
500#[cfg(test)]
501fn test_runner(tests: &[&dyn Fn()]) {
502    unsafe {
503        let (board_kernel, esp32_c3_board, _chip, peripherals) = setup();
504
505        BOARD = Some(board_kernel);
506        PLATFORM = Some(&esp32_c3_board);
507        PERIPHERALS = Some(peripherals);
508        MAIN_CAP = Some(&create_capability!(capabilities::MainLoopCapability));
509
510        PLATFORM.map(|p| {
511            p.watchdog().setup();
512        });
513
514        for test in tests {
515            test();
516        }
517    }
518
519    loop {}
520}