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