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