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