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