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