redboard_redv/
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 Red-V development platform.
6//!
7//! - <https://www.sparkfun.com/products/15594>
8//!
9//! This board is a clone of the Hifive1-revB from SiFive with minor changes.
10
11#![no_std]
12#![no_main]
13
14use core::ptr::addr_of;
15use core::ptr::addr_of_mut;
16
17use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
18use e310_g002::interrupt_service::E310G002DefaultPeripherals;
19use kernel::capabilities;
20use kernel::component::Component;
21use kernel::hil;
22use kernel::hil::led::LedLow;
23use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
24use kernel::platform::{KernelResources, SyscallDriverLookup};
25use kernel::process::ProcessArray;
26use kernel::scheduler::cooperative::CooperativeSched;
27use kernel::utilities::registers::interfaces::ReadWriteable;
28use kernel::{create_capability, debug, static_init};
29use rv32i::csr;
30
31pub mod io;
32
33pub const NUM_PROCS: usize = 4;
34
35/// Static variables used by io.rs.
36static mut PROCESSES: Option<&'static ProcessArray<NUM_PROCS>> = None;
37
38// Reference to the chip for panic dumps.
39static mut CHIP: Option<&'static e310_g002::chip::E310x<E310G002DefaultPeripherals>> = None;
40// Reference to the process printer for panic dumps.
41static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
42    None;
43
44// How should the kernel respond when a process faults.
45const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
46    capsules_system::process_policies::PanicFaultPolicy {};
47
48kernel::stack_size! {0x900}
49
50/// A structure representing this platform that holds references to all
51/// capsules for this platform. We've included an alarm and console.
52struct RedV {
53    led: &'static capsules_core::led::LedDriver<
54        'static,
55        LedLow<'static, sifive::gpio::GpioPin<'static>>,
56        1,
57    >,
58    console: &'static capsules_core::console::Console<'static>,
59    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
60        'static,
61        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
62    >,
63    alarm: &'static capsules_core::alarm::AlarmDriver<
64        'static,
65        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>,
66    >,
67    scheduler: &'static CooperativeSched<'static>,
68    scheduler_timer: &'static VirtualSchedulerTimer<
69        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>,
70    >,
71}
72
73/// Mapping of integer syscalls to objects that implement syscalls.
74impl SyscallDriverLookup for RedV {
75    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
76    where
77        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
78    {
79        match driver_num {
80            capsules_core::led::DRIVER_NUM => f(Some(self.led)),
81            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
82            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
83            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
84            _ => f(None),
85        }
86    }
87}
88
89impl KernelResources<e310_g002::chip::E310x<'static, E310G002DefaultPeripherals<'static>>>
90    for RedV
91{
92    type SyscallDriverLookup = Self;
93    type SyscallFilter = ();
94    type ProcessFault = ();
95    type Scheduler = CooperativeSched<'static>;
96    type SchedulerTimer =
97        VirtualSchedulerTimer<VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>>;
98    type WatchDog = ();
99    type ContextSwitchCallback = ();
100
101    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
102        self
103    }
104    fn syscall_filter(&self) -> &Self::SyscallFilter {
105        &()
106    }
107    fn process_fault(&self) -> &Self::ProcessFault {
108        &()
109    }
110    fn scheduler(&self) -> &Self::Scheduler {
111        self.scheduler
112    }
113    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
114        self.scheduler_timer
115    }
116    fn watchdog(&self) -> &Self::WatchDog {
117        &()
118    }
119    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
120        &()
121    }
122}
123
124/// This is in a separate, inline(never) function so that its stack frame is
125/// removed when this function returns. Otherwise, the stack space used for
126/// these static_inits is wasted.
127#[inline(never)]
128unsafe fn start() -> (
129    &'static kernel::Kernel,
130    RedV,
131    &'static e310_g002::chip::E310x<'static, E310G002DefaultPeripherals<'static>>,
132) {
133    // only machine mode
134    rv32i::configure_trap_handler();
135
136    let peripherals = static_init!(
137        E310G002DefaultPeripherals,
138        E310G002DefaultPeripherals::new(16_000_000)
139    );
140
141    // Setup any recursive dependencies and register deferred calls:
142    peripherals.init();
143
144    // initialize capabilities
145    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
146    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
147
148    peripherals.e310x.watchdog.disable();
149    peripherals.e310x.rtc.disable();
150    peripherals.e310x.pwm0.disable();
151    peripherals.e310x.pwm1.disable();
152    peripherals.e310x.pwm2.disable();
153    peripherals.e310x.uart1.disable();
154
155    peripherals
156        .e310x
157        .prci
158        .set_clock_frequency(sifive::prci::ClockFrequency::Freq16Mhz);
159
160    // Create an array to hold process references.
161    let processes = components::process_array::ProcessArrayComponent::new()
162        .finalize(components::process_array_component_static!(NUM_PROCS));
163    PROCESSES = Some(processes);
164
165    // Setup space to store the core kernel data structure.
166    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
167
168    // Configure kernel debug gpios as early as possible
169    kernel::debug::assign_gpios(
170        Some(&peripherals.e310x.gpio_port[5]), // Blue/only LED
171        None,
172        None,
173    );
174
175    // Create a shared UART channel for the console and for kernel debug.
176    let uart_mux = components::console::UartMuxComponent::new(&peripherals.e310x.uart0, 115200)
177        .finalize(components::uart_mux_component_static!());
178
179    // LEDs
180    let led = components::led::LedsComponent::new().finalize(components::led_component_static!(
181        LedLow<'static, sifive::gpio::GpioPin>,
182        LedLow::new(&peripherals.e310x.gpio_port[5]), // Blue
183    ));
184
185    peripherals.e310x.uart0.initialize_gpio_pins(
186        &peripherals.e310x.gpio_port[17],
187        &peripherals.e310x.gpio_port[16],
188    );
189
190    let hardware_timer = static_init!(
191        e310_g002::chip::E310xClint,
192        e310_g002::chip::E310xClint::new(&e310_g002::clint::CLINT_BASE)
193    );
194
195    // Create a shared virtualization mux layer on top of a single hardware
196    // alarm.
197    let mux_alarm = static_init!(
198        MuxAlarm<'static, e310_g002::chip::E310xClint>,
199        MuxAlarm::new(hardware_timer)
200    );
201    hil::time::Alarm::set_alarm_client(hardware_timer, mux_alarm);
202
203    // Alarm
204    let virtual_alarm_user = static_init!(
205        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint>,
206        VirtualMuxAlarm::new(mux_alarm)
207    );
208    virtual_alarm_user.setup();
209
210    let systick_virtual_alarm = static_init!(
211        VirtualMuxAlarm<'static, e310_g002::chip::E310xClint>,
212        VirtualMuxAlarm::new(mux_alarm)
213    );
214    systick_virtual_alarm.setup();
215
216    let alarm = static_init!(
217        capsules_core::alarm::AlarmDriver<
218            'static,
219            VirtualMuxAlarm<'static, e310_g002::chip::E310xClint>,
220        >,
221        capsules_core::alarm::AlarmDriver::new(
222            virtual_alarm_user,
223            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
224        )
225    );
226    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
227
228    let chip = static_init!(
229        e310_g002::chip::E310x<E310G002DefaultPeripherals>,
230        e310_g002::chip::E310x::new(peripherals, hardware_timer)
231    );
232    CHIP = Some(chip);
233
234    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
235        .finalize(components::process_printer_text_component_static!());
236    PROCESS_PRINTER = Some(process_printer);
237
238    let process_console = components::process_console::ProcessConsoleComponent::new(
239        board_kernel,
240        uart_mux,
241        mux_alarm,
242        process_printer,
243        None,
244    )
245    .finalize(components::process_console_component_static!(
246        e310_g002::chip::E310xClint
247    ));
248    let _ = process_console.start();
249
250    // Need to enable all interrupts for Tock Kernel
251    chip.enable_plic_interrupts();
252
253    // enable interrupts globally
254    csr::CSR
255        .mie
256        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET);
257    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
258
259    // Setup the console.
260    let console = components::console::ConsoleComponent::new(
261        board_kernel,
262        capsules_core::console::DRIVER_NUM,
263        uart_mux,
264    )
265    .finalize(components::console_component_static!());
266    // Create the debugger object that handles calls to `debug!()`.
267    components::debug_writer::DebugWriterComponent::new(
268        uart_mux,
269        create_capability!(capabilities::SetDebugWriterCapability),
270    )
271    .finalize(components::debug_writer_component_static!());
272
273    let lldb = components::lldb::LowLevelDebugComponent::new(
274        board_kernel,
275        capsules_core::low_level_debug::DRIVER_NUM,
276        uart_mux,
277    )
278    .finalize(components::low_level_debug_component_static!());
279
280    // Need two debug!() calls to actually test with QEMU. QEMU seems to have a
281    // much larger UART TX buffer (or it transmits faster). With a single call
282    // the entire message is printed to console even if the kernel loop does not run
283    debug!("Red-V initialization complete.");
284    debug!("Entering main loop.");
285
286    // These symbols are defined in the linker script.
287    extern "C" {
288        /// Beginning of the ROM region containing app images.
289        static _sapps: u8;
290        /// End of the ROM region containing app images.
291        static _eapps: u8;
292        /// Beginning of the RAM region for app memory.
293        static mut _sappmem: u8;
294        /// End of the RAM region for app memory.
295        static _eappmem: u8;
296    }
297
298    let scheduler = components::sched::cooperative::CooperativeComponent::new(processes)
299        .finalize(components::cooperative_component_static!(NUM_PROCS));
300
301    let scheduler_timer = static_init!(
302        VirtualSchedulerTimer<VirtualMuxAlarm<'static, e310_g002::chip::E310xClint<'static>>>,
303        VirtualSchedulerTimer::new(systick_virtual_alarm)
304    );
305
306    let redv = RedV {
307        led,
308        console,
309        lldb,
310        alarm,
311        scheduler,
312        scheduler_timer,
313    };
314
315    kernel::process::load_processes(
316        board_kernel,
317        chip,
318        core::slice::from_raw_parts(
319            addr_of!(_sapps),
320            addr_of!(_eapps) as usize - addr_of!(_sapps) as usize,
321        ),
322        core::slice::from_raw_parts_mut(
323            addr_of_mut!(_sappmem),
324            addr_of!(_eappmem) as usize - addr_of!(_sappmem) as usize,
325        ),
326        &FAULT_RESPONSE,
327        &process_mgmt_cap,
328    )
329    .unwrap_or_else(|err| {
330        debug!("Error loading processes!");
331        debug!("{:?}", err);
332    });
333
334    (board_kernel, redv, chip)
335}
336
337/// Main function called after RAM initialized.
338#[no_mangle]
339pub unsafe fn main() {
340    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
341
342    let (board_kernel, platform, chip) = start();
343    board_kernel.kernel_loop(
344        &platform,
345        chip,
346        None::<&kernel::ipc::IPC<{ NUM_PROCS as u8 }>>,
347        &main_loop_capability,
348    );
349}