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
68kernel::stack_size! {0x1000}
69
70/// A structure representing this platform that holds references to all
71/// capsules for this platform.
72struct RedboardArtemisAtp {
73    alarm: &'static capsules_core::alarm::AlarmDriver<
74        'static,
75        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
76    >,
77    led: &'static capsules_core::led::LedDriver<
78        'static,
79        LedHigh<'static, apollo3::gpio::GpioPin<'static>>,
80        1,
81    >,
82    gpio: &'static capsules_core::gpio::GPIO<'static, apollo3::gpio::GpioPin<'static>>,
83    console: &'static capsules_core::console::Console<'static>,
84    i2c_master_slave: &'static capsules_core::i2c_master_slave_driver::I2CMasterSlaveDriver<
85        'static,
86        capsules_core::i2c_master_slave_combo::I2CMasterSlaveCombo<
87            'static,
88            apollo3::iom::Iom<'static>,
89            apollo3::ios::Ios<'static>,
90        >,
91    >,
92    spi_controller: &'static capsules_core::spi_controller::Spi<
93        'static,
94        capsules_core::virtualizers::virtual_spi::VirtualSpiMasterDevice<
95            'static,
96            apollo3::iom::Iom<'static>,
97        >,
98    >,
99    ble_radio: &'static capsules_extra::ble_advertising_driver::BLE<
100        'static,
101        apollo3::ble::Ble<'static>,
102        VirtualMuxAlarm<'static, apollo3::stimer::STimer<'static>>,
103    >,
104    scheduler: &'static RoundRobinSched<'static>,
105    systick: cortexm4::systick::SysTick,
106}
107
108/// Mapping of integer syscalls to objects that implement syscalls.
109impl SyscallDriverLookup for RedboardArtemisAtp {
110    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
111    where
112        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
113    {
114        match driver_num {
115            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
116            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
117            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
118            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
119            capsules_core::i2c_master_slave_driver::DRIVER_NUM => f(Some(self.i2c_master_slave)),
120            capsules_core::spi_controller::DRIVER_NUM => f(Some(self.spi_controller)),
121            capsules_extra::ble_advertising_driver::DRIVER_NUM => f(Some(self.ble_radio)),
122            _ => f(None),
123        }
124    }
125}
126
127impl KernelResources<apollo3::chip::Apollo3<Apollo3DefaultPeripherals>> for RedboardArtemisAtp {
128    type SyscallDriverLookup = Self;
129    type SyscallFilter = ();
130    type ProcessFault = ();
131    type Scheduler = RoundRobinSched<'static>;
132    type SchedulerTimer = cortexm4::systick::SysTick;
133    type WatchDog = ();
134    type ContextSwitchCallback = ();
135
136    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
137        self
138    }
139    fn syscall_filter(&self) -> &Self::SyscallFilter {
140        &()
141    }
142    fn process_fault(&self) -> &Self::ProcessFault {
143        &()
144    }
145    fn scheduler(&self) -> &Self::Scheduler {
146        self.scheduler
147    }
148    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
149        &self.systick
150    }
151    fn watchdog(&self) -> &Self::WatchDog {
152        &()
153    }
154    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
155        &()
156    }
157}
158
159// Ensure that `setup()` is never inlined
160// This helps reduce the stack frame, see https://github.com/tock/tock/issues/3518
161#[inline(never)]
162unsafe fn setup() -> (
163    &'static kernel::Kernel,
164    &'static RedboardArtemisAtp,
165    &'static apollo3::chip::Apollo3<Apollo3DefaultPeripherals>,
166    &'static Apollo3DefaultPeripherals,
167) {
168    let peripherals = static_init!(Apollo3DefaultPeripherals, Apollo3DefaultPeripherals::new());
169
170    // No need to statically allocate mcu/pwr/clk_ctrl because they are only used in main!
171    let mcu_ctrl = apollo3::mcuctrl::McuCtrl::new();
172    let pwr_ctrl = apollo3::pwrctrl::PwrCtrl::new();
173    let clkgen = apollo3::clkgen::ClkGen::new();
174
175    clkgen.set_clock_frequency(apollo3::clkgen::ClockFrequency::Freq48MHz);
176
177    // initialize capabilities
178    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
179    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
180
181    // Create an array to hold process references.
182    let processes = components::process_array::ProcessArrayComponent::new()
183        .finalize(components::process_array_component_static!(NUM_PROCS));
184    PROCESSES = Some(processes);
185
186    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
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(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        &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}