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