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