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