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