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