hifive_inventor/
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 BBC HiFive Inventor RISC-V development platform.
6//!
7//! - <https://www.hifiveinventor.com/>
8
9#![no_std]
10// Disable this attribute when documenting, as a workaround for
11// https://github.com/rust-lang/rust/issues/62184.
12#![cfg_attr(not(doc), no_main)]
13
14use core::ptr::{addr_of, addr_of_mut};
15
16use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
17use e310_g003::interrupt_service::E310G003DefaultPeripherals;
18use kernel::capabilities;
19use kernel::component::Component;
20use kernel::hil;
21use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
22use kernel::platform::{KernelResources, SyscallDriverLookup};
23use kernel::scheduler::cooperative::CooperativeSched;
24use kernel::utilities::registers::interfaces::ReadWriteable;
25use kernel::{create_capability, debug, static_init};
26use rv32i::csr;
27
28pub mod io;
29
30pub const NUM_PROCS: usize = 4;
31//
32// Actual memory for holding the active process structures. Need an empty list
33// at least.
34static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
35    [None; NUM_PROCS];
36
37// Reference to the chip for panic dumps.
38static mut CHIP: Option<&'static e310_g003::chip::E310x<E310G003DefaultPeripherals>> = None;
39// Reference to the process printer for panic dumps.
40static mut PROCESS_PRINTER: Option<&'static capsules_system::process_printer::ProcessPrinterText> =
41    None;
42
43// How should the kernel respond when a process faults.
44const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
45    capsules_system::process_policies::PanicFaultPolicy {};
46
47/// Dummy buffer that causes the linker to reserve enough space for the stack.
48#[no_mangle]
49#[link_section = ".stack_buffer"]
50pub static mut STACK_MEMORY: [u8; 0x1500] = [0; 0x1500];
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 HiFiveInventor {
55    console: &'static capsules_core::console::Console<'static>,
56    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
57        'static,
58        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
59    >,
60    alarm: &'static capsules_core::alarm::AlarmDriver<
61        'static,
62        VirtualMuxAlarm<'static, e310_g003::chip::E310xClint<'static>>,
63    >,
64    scheduler: &'static CooperativeSched<'static>,
65    scheduler_timer: &'static VirtualSchedulerTimer<
66        VirtualMuxAlarm<'static, e310_g003::chip::E310xClint<'static>>,
67    >,
68}
69
70/// Mapping of integer syscalls to objects that implement syscalls.
71impl SyscallDriverLookup for HiFiveInventor {
72    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
73    where
74        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
75    {
76        match driver_num {
77            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
78            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
79            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
80            _ => f(None),
81        }
82    }
83}
84
85impl KernelResources<e310_g003::chip::E310x<'static, E310G003DefaultPeripherals<'static>>>
86    for HiFiveInventor
87{
88    type SyscallDriverLookup = Self;
89    type SyscallFilter = ();
90    type ProcessFault = ();
91    type Scheduler = CooperativeSched<'static>;
92    type SchedulerTimer =
93        VirtualSchedulerTimer<VirtualMuxAlarm<'static, e310_g003::chip::E310xClint<'static>>>;
94    type WatchDog = ();
95    type ContextSwitchCallback = ();
96
97    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
98        self
99    }
100    fn syscall_filter(&self) -> &Self::SyscallFilter {
101        &()
102    }
103    fn process_fault(&self) -> &Self::ProcessFault {
104        &()
105    }
106    fn scheduler(&self) -> &Self::Scheduler {
107        self.scheduler
108    }
109    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
110        self.scheduler_timer
111    }
112    fn watchdog(&self) -> &Self::WatchDog {
113        &()
114    }
115    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
116        &()
117    }
118}
119
120/// This is in a separate, inline(never) function so that its stack frame is
121/// removed when this function returns. Otherwise, the stack space used for
122/// these static_inits is wasted.
123#[inline(never)]
124unsafe fn start() -> (
125    &'static kernel::Kernel,
126    HiFiveInventor,
127    &'static e310_g003::chip::E310x<'static, E310G003DefaultPeripherals<'static>>,
128) {
129    // only machine mode
130    rv32i::configure_trap_handler();
131
132    let peripherals = static_init!(
133        E310G003DefaultPeripherals,
134        E310G003DefaultPeripherals::new(16_000_000)
135    );
136
137    // Setup any recursive dependencies and register deferred calls:
138    peripherals.init();
139
140    peripherals
141        .e310x
142        .prci
143        .set_clock_frequency(sifive::prci::ClockFrequency::Freq16Mhz);
144
145    peripherals.e310x.uart0.initialize_gpio_pins(
146        &peripherals.e310x.gpio_port[17],
147        &peripherals.e310x.gpio_port[16],
148    );
149    peripherals.e310x.uart1.initialize_gpio_pins(
150        &peripherals.e310x.gpio_port[18],
151        &peripherals.e310x.gpio_port[23],
152    );
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    // initialize capabilities
162    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
163    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
164
165    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
166
167    // Configure kernel debug gpios as early as possible
168    kernel::debug::assign_gpios(None, None, None);
169
170    // Create a shared UART channel for the console and for kernel debug.
171    let uart_mux = components::console::UartMuxComponent::new(&peripherals.e310x.uart0, 115200)
172        .finalize(components::uart_mux_component_static!());
173
174    let hardware_timer = static_init!(
175        e310_g003::chip::E310xClint,
176        e310_g003::chip::E310xClint::new(&e310_g003::clint::CLINT_BASE)
177    );
178
179    // Create a shared virtualization mux layer on top of a single hardware
180    // alarm.
181    let mux_alarm = static_init!(
182        MuxAlarm<'static, e310_g003::chip::E310xClint>,
183        MuxAlarm::new(hardware_timer)
184    );
185    hil::time::Alarm::set_alarm_client(hardware_timer, mux_alarm);
186
187    // Alarm
188    let virtual_alarm_user = static_init!(
189        VirtualMuxAlarm<'static, e310_g003::chip::E310xClint>,
190        VirtualMuxAlarm::new(mux_alarm)
191    );
192    virtual_alarm_user.setup();
193
194    let systick_virtual_alarm = static_init!(
195        VirtualMuxAlarm<'static, e310_g003::chip::E310xClint>,
196        VirtualMuxAlarm::new(mux_alarm)
197    );
198    systick_virtual_alarm.setup();
199
200    let alarm = static_init!(
201        capsules_core::alarm::AlarmDriver<
202            'static,
203            VirtualMuxAlarm<'static, e310_g003::chip::E310xClint>,
204        >,
205        capsules_core::alarm::AlarmDriver::new(
206            virtual_alarm_user,
207            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
208        )
209    );
210    hil::time::Alarm::set_alarm_client(virtual_alarm_user, alarm);
211
212    let chip = static_init!(
213        e310_g003::chip::E310x<E310G003DefaultPeripherals>,
214        e310_g003::chip::E310x::new(peripherals, hardware_timer)
215    );
216    CHIP = Some(chip);
217
218    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
219        .finalize(components::process_printer_text_component_static!());
220    PROCESS_PRINTER = Some(process_printer);
221
222    let process_console = components::process_console::ProcessConsoleComponent::new(
223        board_kernel,
224        uart_mux,
225        mux_alarm,
226        process_printer,
227        None,
228    )
229    .finalize(components::process_console_component_static!(
230        e310_g003::chip::E310xClint
231    ));
232    let _ = process_console.start();
233
234    // Need to enable all interrupts for Tock Kernel
235    chip.enable_plic_interrupts();
236
237    // enable interrupts globally
238    csr::CSR
239        .mie
240        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET + csr::mie::mie::mtimer::SET);
241    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
242
243    // Setup the console.
244    let console = components::console::ConsoleComponent::new(
245        board_kernel,
246        capsules_core::console::DRIVER_NUM,
247        uart_mux,
248    )
249    .finalize(components::console_component_static!());
250    // Create the debugger object that handles calls to `debug!()`.
251    components::debug_writer::DebugWriterComponent::new(uart_mux)
252        .finalize(components::debug_writer_component_static!());
253
254    let lldb = components::lldb::LowLevelDebugComponent::new(
255        board_kernel,
256        capsules_core::low_level_debug::DRIVER_NUM,
257        uart_mux,
258    )
259    .finalize(components::low_level_debug_component_static!());
260
261    debug!("HiFive1 initialization complete. Entering main loop.");
262
263    // These symbols are defined in the linker script.
264    extern "C" {
265        /// Beginning of the ROM region containing app images.
266        static _sapps: u8;
267        /// End of the ROM region containing app images.
268        static _eapps: u8;
269        /// Beginning of the RAM region for app memory.
270        static mut _sappmem: u8;
271        /// End of the RAM region for app memory.
272        static _eappmem: u8;
273    }
274
275    let scheduler =
276        components::sched::cooperative::CooperativeComponent::new(&*addr_of!(PROCESSES))
277            .finalize(components::cooperative_component_static!(NUM_PROCS));
278
279    let scheduler_timer = static_init!(
280        VirtualSchedulerTimer<VirtualMuxAlarm<'static, e310_g003::chip::E310xClint<'static>>>,
281        VirtualSchedulerTimer::new(systick_virtual_alarm)
282    );
283    let hifive1 = HiFiveInventor {
284        console,
285        lldb,
286        alarm,
287        scheduler,
288        scheduler_timer,
289    };
290    kernel::process::load_processes(
291        board_kernel,
292        chip,
293        core::slice::from_raw_parts(
294            core::ptr::addr_of!(_sapps),
295            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
296        ),
297        core::slice::from_raw_parts_mut(
298            core::ptr::addr_of_mut!(_sappmem),
299            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
300        ),
301        &mut *addr_of_mut!(PROCESSES),
302        &FAULT_RESPONSE,
303        &process_mgmt_cap,
304    )
305    .unwrap_or_else(|err| {
306        debug!("Error loading processes!");
307        debug!("{:?}", err);
308    });
309
310    (board_kernel, hifive1, chip)
311}
312
313/// Main function called after RAM initialized.
314#[no_mangle]
315pub unsafe fn main() {
316    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
317
318    let (board_kernel, board, chip) = start();
319    board_kernel.kernel_loop(
320        &board,
321        chip,
322        None::<&kernel::ipc::IPC<0>>,
323        &main_loop_capability,
324    );
325}