esp32_c3_board/
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 ESP32-C3 RISC-V development platform.
6//!
7
8#![no_std]
9#![no_main]
10#![feature(custom_test_frameworks)]
11#![test_runner(test_runner)]
12#![reexport_test_harness_main = "test_main"]
13
14use core::ptr::{addr_of, addr_of_mut};
15
16use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
17use esp32_c3::chip::Esp32C3DefaultPeripherals;
18use kernel::capabilities;
19use kernel::component::Component;
20use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
21use kernel::platform::{KernelResources, SyscallDriverLookup};
22use kernel::scheduler::priority::PrioritySched;
23use kernel::utilities::registers::interfaces::ReadWriteable;
24use kernel::{create_capability, debug, hil, static_init};
25use rv32i::csr;
26
27pub mod io;
28
29#[cfg(test)]
30mod tests;
31
32const NUM_PROCS: usize = 4;
33//
34// Actual memory for holding the active process structures. Need an empty list
35// at least.
36static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
37    [None; NUM_PROCS];
38
39// Reference to the chip for panic dumps.
40static mut CHIP: Option<&'static esp32_c3::chip::Esp32C3<Esp32C3DefaultPeripherals>> = None;
41// Static reference to process printer for panic dumps.
42static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
43    None;
44
45// How should the kernel respond when a process faults.
46const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
47    capsules_system::process_policies::PanicFaultPolicy {};
48
49// Test access to the peripherals
50#[cfg(test)]
51static mut PERIPHERALS: Option<&'static Esp32C3DefaultPeripherals> = None;
52// Test access to scheduler
53#[cfg(test)]
54static mut SCHEDULER: Option<&PrioritySched> = 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 Esp32C3Board> = 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, esp32_c3::timg::TimG<'static>>> = None;
66
67/// Dummy buffer that causes the linker to reserve enough space for the stack.
68#[no_mangle]
69#[link_section = ".stack_buffer"]
70pub static mut STACK_MEMORY: [u8; 0x900] = [0; 0x900];
71
72type RngDriver = components::rng::RngComponentType<esp32_c3::rng::Rng<'static>>;
73
74/// A structure representing this platform that holds references to all
75/// capsules for this platform. We've included an alarm and console.
76struct Esp32C3Board {
77    gpio: &'static capsules_core::gpio::GPIO<'static, esp32::gpio::GpioPin<'static>>,
78    console: &'static capsules_core::console::Console<'static>,
79    alarm: &'static capsules_core::alarm::AlarmDriver<
80        'static,
81        VirtualMuxAlarm<'static, esp32_c3::timg::TimG<'static>>,
82    >,
83    scheduler: &'static PrioritySched,
84    scheduler_timer: &'static VirtualSchedulerTimer<esp32_c3::timg::TimG<'static>>,
85    rng: &'static RngDriver,
86}
87
88/// Mapping of integer syscalls to objects that implement syscalls.
89impl SyscallDriverLookup for Esp32C3Board {
90    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
91    where
92        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
93    {
94        match driver_num {
95            capsules_core::gpio::DRIVER_NUM => f(Some(self.gpio)),
96            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
97            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
98            capsules_core::rng::DRIVER_NUM => f(Some(self.rng)),
99            _ => f(None),
100        }
101    }
102}
103
104impl KernelResources<esp32_c3::chip::Esp32C3<'static, Esp32C3DefaultPeripherals<'static>>>
105    for Esp32C3Board
106{
107    type SyscallDriverLookup = Self;
108    type SyscallFilter = ();
109    type ProcessFault = ();
110    type ContextSwitchCallback = ();
111    type Scheduler = PrioritySched;
112    type SchedulerTimer = VirtualSchedulerTimer<esp32_c3::timg::TimG<'static>>;
113    type WatchDog = ();
114
115    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
116        self
117    }
118    fn syscall_filter(&self) -> &Self::SyscallFilter {
119        &()
120    }
121    fn process_fault(&self) -> &Self::ProcessFault {
122        &()
123    }
124    fn scheduler(&self) -> &Self::Scheduler {
125        self.scheduler
126    }
127    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
128        self.scheduler_timer
129    }
130    fn watchdog(&self) -> &Self::WatchDog {
131        &()
132    }
133    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
134        &()
135    }
136}
137
138unsafe fn setup() -> (
139    &'static kernel::Kernel,
140    &'static Esp32C3Board,
141    &'static esp32_c3::chip::Esp32C3<'static, Esp32C3DefaultPeripherals<'static>>,
142    &'static Esp32C3DefaultPeripherals<'static>,
143) {
144    use esp32_c3::sysreg::{CpuFrequency, PllFrequency};
145
146    // only machine mode
147    rv32i::configure_trap_handler();
148
149    let peripherals = static_init!(Esp32C3DefaultPeripherals, Esp32C3DefaultPeripherals::new());
150
151    peripherals.timg0.disable_wdt();
152    peripherals.rtc_cntl.disable_wdt();
153    peripherals.rtc_cntl.disable_super_wdt();
154    peripherals.rtc_cntl.enable_fosc();
155    peripherals.sysreg.disable_timg0();
156    peripherals.sysreg.enable_timg0();
157
158    peripherals
159        .sysreg
160        .use_pll_clock_source(PllFrequency::MHz320, CpuFrequency::MHz160);
161
162    // initialise capabilities
163    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
164    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
165
166    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
167
168    // Configure kernel debug gpios as early as possible
169    kernel::debug::assign_gpios(None, None, None);
170
171    // Create a shared UART channel for the console and for kernel debug.
172    let uart_mux = components::console::UartMuxComponent::new(&peripherals.uart0, 115200)
173        .finalize(components::uart_mux_component_static!());
174
175    let gpio = components::gpio::GpioComponent::new(
176        board_kernel,
177        capsules_core::gpio::DRIVER_NUM,
178        components::gpio_component_helper!(
179            esp32::gpio::GpioPin,
180            0 => &peripherals.gpio[0],
181            1 => &peripherals.gpio[1],
182            2 => &peripherals.gpio[2],
183            3 => &peripherals.gpio[3],
184            4 => &peripherals.gpio[4],
185            5 => &peripherals.gpio[5],
186            6 => &peripherals.gpio[6],
187            7 => &peripherals.gpio[7],
188            8 => &peripherals.gpio[15]
189        ),
190    )
191    .finalize(components::gpio_component_static!(esp32::gpio::GpioPin));
192
193    // Create a shared virtualization mux layer on top of a single hardware
194    // alarm.
195    let mux_alarm = static_init!(
196        MuxAlarm<'static, esp32_c3::timg::TimG>,
197        MuxAlarm::new(&peripherals.timg0)
198    );
199    hil::time::Alarm::set_alarm_client(&peripherals.timg0, mux_alarm);
200
201    ALARM = Some(mux_alarm);
202
203    // Alarm
204    let virtual_alarm_user = static_init!(
205        VirtualMuxAlarm<'static, esp32_c3::timg::TimG>,
206        VirtualMuxAlarm::new(mux_alarm)
207    );
208    virtual_alarm_user.setup();
209
210    let alarm = static_init!(
211        capsules_core::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, esp32_c3::timg::TimG>>,
212        capsules_core::alarm::AlarmDriver::new(
213            virtual_alarm_user,
214            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
215        )
216    );
217    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
218
219    let scheduler_timer = static_init!(
220        VirtualSchedulerTimer<esp32_c3::timg::TimG<'static>>,
221        VirtualSchedulerTimer::new(&peripherals.timg1)
222    );
223
224    let chip = static_init!(
225        esp32_c3::chip::Esp32C3<
226            Esp32C3DefaultPeripherals,
227        >,
228        esp32_c3::chip::Esp32C3::new(peripherals)
229    );
230    CHIP = Some(chip);
231
232    // Need to enable all interrupts for Tock Kernel
233    chip.map_pic_interrupts();
234    chip.enable_pic_interrupts();
235
236    // enable interrupts globally
237    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
238
239    // Setup the console.
240    let console = components::console::ConsoleComponent::new(
241        board_kernel,
242        capsules_core::console::DRIVER_NUM,
243        uart_mux,
244    )
245    .finalize(components::console_component_static!());
246    // Create the debugger object that handles calls to `debug!()`.
247    components::debug_writer::DebugWriterComponent::new(
248        uart_mux,
249        create_capability!(capabilities::SetDebugWriterCapability),
250    )
251    .finalize(components::debug_writer_component_static!());
252
253    // Create process printer for panic.
254    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
255        .finalize(components::process_printer_text_component_static!());
256    PROCESS_PRINTER = Some(process_printer);
257
258    debug!("ESP32-C3 initialisation complete.");
259    debug!("Entering main loop.");
260
261    // These symbols are defined in the linker script.
262    extern "C" {
263        /// Beginning of the ROM region containing app images.
264        static _sapps: u8;
265        /// End of the ROM region containing app images.
266        static _eapps: u8;
267        /// Beginning of the RAM region for app memory.
268        static mut _sappmem: u8;
269        /// End of the RAM region for app memory.
270        static _eappmem: u8;
271    }
272
273    let scheduler = components::sched::priority::PriorityComponent::new(board_kernel)
274        .finalize(components::priority_component_static!());
275
276    // PROCESS CONSOLE
277    let process_console = components::process_console::ProcessConsoleComponent::new(
278        board_kernel,
279        uart_mux,
280        mux_alarm,
281        process_printer,
282        None,
283    )
284    .finalize(components::process_console_component_static!(
285        esp32_c3::timg::TimG
286    ));
287    let _ = process_console.start();
288
289    let rng = components::rng::RngComponent::new(
290        board_kernel,
291        capsules_core::rng::DRIVER_NUM,
292        &peripherals.rng,
293    )
294    .finalize(components::rng_component_static!(esp32_c3::rng::Rng));
295
296    let esp32_c3_board = static_init!(
297        Esp32C3Board,
298        Esp32C3Board {
299            gpio,
300            console,
301            alarm,
302            scheduler,
303            scheduler_timer,
304            rng,
305        }
306    );
307
308    kernel::process::load_processes(
309        board_kernel,
310        chip,
311        core::slice::from_raw_parts(
312            core::ptr::addr_of!(_sapps),
313            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
314        ),
315        core::slice::from_raw_parts_mut(
316            core::ptr::addr_of_mut!(_sappmem),
317            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
318        ),
319        &mut *addr_of_mut!(PROCESSES),
320        &FAULT_RESPONSE,
321        &process_mgmt_cap,
322    )
323    .unwrap_or_else(|err| {
324        debug!("Error loading processes!");
325        debug!("{:?}", err);
326    });
327
328    peripherals.init();
329
330    (board_kernel, esp32_c3_board, chip, peripherals)
331}
332
333/// Main function.
334///
335/// This function is called from the arch crate after some very basic RISC-V
336/// setup and RAM initialization.
337#[no_mangle]
338pub unsafe fn main() {
339    #[cfg(test)]
340    test_main();
341
342    #[cfg(not(test))]
343    {
344        let (board_kernel, esp32_c3_board, chip, _peripherals) = setup();
345
346        let main_loop_cap = create_capability!(capabilities::MainLoopCapability);
347
348        board_kernel.kernel_loop(
349            esp32_c3_board,
350            chip,
351            None::<&kernel::ipc::IPC<0>>,
352            &main_loop_cap,
353        );
354    }
355}
356
357#[cfg(test)]
358use kernel::platform::watchdog::WatchDog;
359
360#[cfg(test)]
361fn test_runner(tests: &[&dyn Fn()]) {
362    unsafe {
363        let (board_kernel, esp32_c3_board, _chip, peripherals) = setup();
364
365        BOARD = Some(board_kernel);
366        PLATFORM = Some(&esp32_c3_board);
367        PERIPHERALS = Some(peripherals);
368        SCHEDULER = Some(
369            components::sched::priority::PriorityComponent::new(board_kernel)
370                .finalize(components::priority_component_static!()),
371        );
372        MAIN_CAP = Some(&create_capability!(capabilities::MainLoopCapability));
373
374        PLATFORM.map(|p| {
375            p.watchdog().setup();
376        });
377
378        for test in tests {
379            test();
380        }
381    }
382
383    loop {}
384}