litex_arty/
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 a LiteX-built VexRiscv-based SoC synthesized for a
6//! Digilent Arty-A7 FPGA board
7
8#![no_std]
9#![no_main]
10
11use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
12use kernel::capabilities;
13use kernel::component::Component;
14use kernel::debug::PanicResources;
15use kernel::hil::time::{Alarm, Timer};
16use kernel::platform::chip::InterruptService;
17use kernel::platform::{KernelResources, SyscallDriverLookup};
18use kernel::scheduler::mlfq::MLFQSched;
19use kernel::utilities::registers::interfaces::ReadWriteable;
20use kernel::utilities::single_thread_value::SingleThreadValue;
21use kernel::utilities::StaticRef;
22use kernel::{create_capability, debug, static_init};
23use rv32i::csr;
24
25mod io;
26mod litex_generated_constants;
27
28// This module contains the LiteX SoC configuration options, register
29// positions, interrupt mappings and other implementation details of
30// the generated bitstream.
31//
32// Its values are used throughout the file, hence import it under a
33// short name.
34use litex_generated_constants as socc;
35
36/// Structure for dynamic interrupt mapping, depending on the SoC
37/// configuration
38///
39/// This struct is deliberately kept in the board crate. Because of
40/// the configurable nature of LiteX, it does not make sense to define
41/// a default interrupt mapping, as the interrupt numbers are
42/// generated sequentially for all softcores.
43struct LiteXArtyInterruptablePeripherals {
44    uart0: &'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>,
45    timer0: &'static litex_vexriscv::timer::LiteXTimer<
46        'static,
47        socc::SoCRegisterFmt,
48        socc::ClockFrequency,
49    >,
50    ethmac0: &'static litex_vexriscv::liteeth::LiteEth<
51        'static,
52        { socc::ETHMAC_TX_SLOTS },
53        socc::SoCRegisterFmt,
54    >,
55}
56
57impl LiteXArtyInterruptablePeripherals {
58    // Resolve any recursive dependencies and set up deferred calls:
59    pub fn init(&'static self) {
60        kernel::deferred_call::DeferredCallClient::register(self.uart0);
61    }
62}
63
64impl InterruptService for LiteXArtyInterruptablePeripherals {
65    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
66        match interrupt as usize {
67            socc::UART_INTERRUPT => {
68                self.uart0.service_interrupt();
69                true
70            }
71            socc::TIMER0_INTERRUPT => {
72                self.timer0.service_interrupt();
73                true
74            }
75            socc::ETHMAC_INTERRUPT => {
76                self.ethmac0.service_interrupt();
77                true
78            }
79            _ => false,
80        }
81    }
82}
83
84const NUM_PROCS: usize = 4;
85
86type ChipHw = litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>;
87type AlarmHw =
88    litex_vexriscv::timer::LiteXAlarm<'static, 'static, socc::SoCRegisterFmt, socc::ClockFrequency>;
89type SchedulerTimerHw =
90    components::virtual_scheduler_timer::VirtualSchedulerTimerComponentType<AlarmHw>;
91type ProcessPrinterInUse = capsules_system::process_printer::ProcessPrinterText;
92
93/// Resources for when a board panics used by io.rs.
94static PANIC_RESOURCES: SingleThreadValue<PanicResources<ChipHw, ProcessPrinterInUse>> =
95    SingleThreadValue::new(PanicResources::new());
96
97// How should the kernel respond when a process faults.
98const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
99    capsules_system::process_policies::PanicFaultPolicy {};
100
101kernel::stack_size! {0x2000}
102
103/// A structure representing this platform that holds references to all
104/// capsules for this platform.
105struct LiteXArty {
106    led_driver: &'static capsules_core::led::LedDriver<
107        'static,
108        litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
109        4,
110    >,
111    console: &'static capsules_core::console::Console<'static>,
112    pconsole: &'static capsules_core::process_console::ProcessConsole<
113        'static,
114        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
115        VirtualMuxAlarm<'static, AlarmHw>,
116        components::process_console::Capability,
117    >,
118    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
119        'static,
120        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
121    >,
122    alarm: &'static capsules_core::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, AlarmHw>>,
123    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
124    scheduler: &'static MLFQSched<'static, VirtualMuxAlarm<'static, AlarmHw>>,
125    scheduler_timer: &'static SchedulerTimerHw,
126}
127
128/// Mapping of integer syscalls to objects that implement syscalls
129impl SyscallDriverLookup for LiteXArty {
130    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
131    where
132        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
133    {
134        match driver_num {
135            capsules_core::led::DRIVER_NUM => f(Some(self.led_driver)),
136            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
137            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
138            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
139            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
140            _ => f(None),
141        }
142    }
143}
144
145impl KernelResources<litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>
146    for LiteXArty
147{
148    type SyscallDriverLookup = Self;
149    type SyscallFilter = ();
150    type ProcessFault = ();
151    type Scheduler = MLFQSched<'static, VirtualMuxAlarm<'static, AlarmHw>>;
152    type SchedulerTimer = SchedulerTimerHw;
153    type WatchDog = ();
154    type ContextSwitchCallback = ();
155
156    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
157        self
158    }
159    fn syscall_filter(&self) -> &Self::SyscallFilter {
160        &()
161    }
162    fn process_fault(&self) -> &Self::ProcessFault {
163        &()
164    }
165    fn scheduler(&self) -> &Self::Scheduler {
166        self.scheduler
167    }
168    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
169        self.scheduler_timer
170    }
171    fn watchdog(&self) -> &Self::WatchDog {
172        &()
173    }
174    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
175        &()
176    }
177}
178
179/// This is in a separate, inline(never) function so that its stack frame is
180/// removed when this function returns. Otherwise, the stack space used for
181/// these static_inits is wasted.
182#[inline(never)]
183unsafe fn start() -> (
184    &'static kernel::Kernel,
185    LiteXArty,
186    &'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>,
187) {
188    // These symbols are defined in the linker script.
189    extern "C" {
190        /// Beginning of the ROM region containing app images.
191        static _sapps: u8;
192        /// End of the ROM region containing app images.
193        static _eapps: u8;
194        /// Beginning of the RAM region for app memory.
195        static mut _sappmem: u8;
196        /// End of the RAM region for app memory.
197        static _eappmem: u8;
198        /// The start of the kernel text (Included only for kernel PMP)
199        static _stext: u8;
200        /// The end of the kernel text (Included only for kernel PMP)
201        static _etext: u8;
202        /// The start of the kernel / app / storage flash (Included only for kernel PMP)
203        static _sflash: u8;
204        /// The end of the kernel / app / storage flash (Included only for kernel PMP)
205        static _eflash: u8;
206        /// The start of the kernel / app RAM (Included only for kernel PMP)
207        static _ssram: u8;
208        /// The end of the kernel / app RAM (Included only for kernel PMP)
209        static _esram: u8;
210    }
211
212    // ---------- BASIC INITIALIZATION ----------
213
214    // Basic setup of the riscv platform.
215    rv32i::configure_trap_handler();
216
217    // Initialize deferred calls very early.
218    kernel::deferred_call::initialize_deferred_call_state_unsafe::<
219        <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
220    >();
221
222    // Bind global variables to this thread.
223    PANIC_RESOURCES
224        .bind_to_thread_unsafe::<<ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider>();
225
226    // Set up memory protection immediately after setting the trap handler, to
227    // ensure that much of the board initialization routine runs with PMP kernel
228    // memory protection.
229    let pmp = rv32i::pmp::kernel_protection::KernelProtectionPMP::new(
230        rv32i::pmp::kernel_protection::FlashRegion(
231            rv32i::pmp::NAPOTRegionSpec::from_start_end(
232                core::ptr::addr_of!(_sflash),
233                core::ptr::addr_of!(_eflash),
234            )
235            .unwrap(),
236        ),
237        rv32i::pmp::kernel_protection::RAMRegion(
238            rv32i::pmp::NAPOTRegionSpec::from_start_end(
239                core::ptr::addr_of!(_ssram),
240                core::ptr::addr_of!(_esram),
241            )
242            .unwrap(),
243        ),
244        rv32i::pmp::kernel_protection::MMIORegion(
245            rv32i::pmp::NAPOTRegionSpec::from_start_size(
246                0xf0000000 as *const u8, // start
247                0x10000000,              // size
248            )
249            .unwrap(),
250        ),
251        rv32i::pmp::kernel_protection::KernelTextRegion(
252            rv32i::pmp::TORRegionSpec::from_start_end(
253                core::ptr::addr_of!(_stext),
254                core::ptr::addr_of!(_etext),
255            )
256            .unwrap(),
257        ),
258    )
259    .unwrap();
260
261    // initialize capabilities
262    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
263    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
264
265    // Create an array to hold process references.
266    let processes = components::process_array::ProcessArrayComponent::new()
267        .finalize(components::process_array_component_static!(NUM_PROCS));
268    PANIC_RESOURCES.get().map(|resources| {
269        resources.processes.put(processes.as_slice());
270    });
271
272    // Setup space to store the core kernel data structure.
273    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(processes.as_slice()));
274
275    // ---------- LED CONTROLLER HARDWARE ----------
276
277    // Initialize the LEDs, stopping any patterns from the bootloader
278    // / bios still running in HW and turn them all off
279    let led0 = static_init!(
280        litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>,
281        litex_vexriscv::led_controller::LiteXLedController::new(
282            StaticRef::new(
283                socc::CSR_LEDS_BASE
284                    as *const litex_vexriscv::led_controller::LiteXLedRegisters<
285                        socc::SoCRegisterFmt,
286                    >
287            ),
288            4, // 4 LEDs on this board
289        )
290    );
291    led0.initialize();
292
293    // --------- TIMER & UPTIME CORE; ALARM INITIALIZATION ----------
294
295    // Initialize the hardware timer
296    let timer0 = static_init!(
297        litex_vexriscv::timer::LiteXTimer<'static, socc::SoCRegisterFmt, socc::ClockFrequency>,
298        litex_vexriscv::timer::LiteXTimer::new(StaticRef::new(
299            socc::CSR_TIMER0_BASE
300                as *const litex_vexriscv::timer::LiteXTimerRegisters<socc::SoCRegisterFmt>
301        ),)
302    );
303
304    // The SoC is expected to feature the 64-bit uptime extension to the timer hardware
305    let timer0_uptime = static_init!(
306        litex_vexriscv::timer::LiteXTimerUptime<
307            'static,
308            socc::SoCRegisterFmt,
309            socc::ClockFrequency,
310        >,
311        litex_vexriscv::timer::LiteXTimerUptime::new(timer0)
312    );
313
314    // Create the LiteXAlarm based on the hardware LiteXTimer core and
315    // the uptime peripheral
316    let litex_alarm = static_init!(
317        AlarmHw,
318        litex_vexriscv::timer::LiteXAlarm::new(timer0_uptime, timer0)
319    );
320    timer0.set_timer_client(litex_alarm);
321    litex_alarm.initialize();
322
323    // Create a shared virtualization mux layer on top of a single hardware
324    // alarm.
325    let mux_alarm = static_init!(MuxAlarm<'static, AlarmHw>, MuxAlarm::new(litex_alarm));
326    litex_alarm.set_alarm_client(mux_alarm);
327
328    // Userspace alarm driver
329    let virtual_alarm_user = static_init!(
330        VirtualMuxAlarm<'static, AlarmHw>,
331        VirtualMuxAlarm::new(mux_alarm)
332    );
333    virtual_alarm_user.setup();
334
335    let alarm = static_init!(
336        capsules_core::alarm::AlarmDriver<'static, VirtualMuxAlarm<'static, AlarmHw>>,
337        capsules_core::alarm::AlarmDriver::new(
338            virtual_alarm_user,
339            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
340        )
341    );
342    virtual_alarm_user.set_alarm_client(alarm);
343
344    let scheduler_timer =
345        components::virtual_scheduler_timer::VirtualSchedulerTimerComponent::new(mux_alarm)
346            .finalize(components::virtual_scheduler_timer_component_static!(
347                AlarmHw
348            ));
349
350    // ---------- UART ----------
351
352    // Initialize the HW UART
353    let uart0 = static_init!(
354        litex_vexriscv::uart::LiteXUart<socc::SoCRegisterFmt>,
355        litex_vexriscv::uart::LiteXUart::new(
356            StaticRef::new(
357                socc::CSR_UART_BASE
358                    as *const litex_vexriscv::uart::LiteXUartRegisters<socc::SoCRegisterFmt>,
359            ),
360            // No UART PHY CSR present, thus baudrate fixed in
361            // hardware. Change with --uart-baudrate during SoC
362            // generation. Fixed to 1MBd.
363            None,
364        )
365    );
366    uart0.initialize();
367
368    // Create a shared UART channel for the console and for kernel debug.
369    let uart_mux = components::console::UartMuxComponent::new(uart0, socc::UART_BAUDRATE)
370        .finalize(components::uart_mux_component_static!());
371
372    // ---------- ETHERNET ----------
373
374    // ETHMAC peripheral
375    let ethmac0 = static_init!(
376        litex_vexriscv::liteeth::LiteEth<{socc::ETHMAC_TX_SLOTS}, socc::SoCRegisterFmt>,
377        litex_vexriscv::liteeth::LiteEth::new(
378            StaticRef::new(
379                socc::CSR_ETHMAC_BASE
380                    as *const litex_vexriscv::liteeth::LiteEthMacRegisters<socc::SoCRegisterFmt>,
381            ),
382            socc::MEM_ETHMAC_BASE,
383            socc::MEM_ETHMAC_SIZE,
384            socc::ETHMAC_SLOT_SIZE,
385            socc::ETHMAC_RX_SLOTS,
386            socc::ETHMAC_TX_SLOTS,
387        )
388    );
389
390    // Initialize the ETHMAC controller
391    ethmac0.initialize();
392
393    // ---------- LED DRIVER ----------
394
395    // LEDs
396    let led_driver =
397        components::led::LedsComponent::new().finalize(components::led_component_static!(
398            litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
399            led0.get_led(0).unwrap(),
400            led0.get_led(1).unwrap(),
401            led0.get_led(2).unwrap(),
402            led0.get_led(3).unwrap(),
403        ));
404
405    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ----------
406
407    let interrupt_service = static_init!(
408        LiteXArtyInterruptablePeripherals,
409        LiteXArtyInterruptablePeripherals {
410            uart0,
411            timer0,
412            ethmac0,
413        }
414    );
415    interrupt_service.init();
416
417    let chip = static_init!(
418        litex_vexriscv::chip::LiteXVexRiscv<
419            LiteXArtyInterruptablePeripherals,
420        >,
421        litex_vexriscv::chip::LiteXVexRiscv::new(
422            "LiteX on Arty A7",
423            interrupt_service,
424            pmp,
425        )
426    );
427    PANIC_RESOURCES.get().map(|resources| {
428        resources.chip.put(chip);
429    });
430
431    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
432        .finalize(components::process_printer_text_component_static!());
433    PANIC_RESOURCES.get().map(|resources| {
434        resources.printer.put(process_printer);
435    });
436
437    // Enable RISC-V interrupts globally
438    csr::CSR
439        .mie
440        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET);
441    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
442
443    // Unmask all interrupt sources in the interrupt controller
444    chip.unmask_interrupts();
445
446    // Setup the process console.
447    let pconsole = components::process_console::ProcessConsoleComponent::new(
448        board_kernel,
449        uart_mux,
450        mux_alarm,
451        process_printer,
452        None,
453    )
454    .finalize(components::process_console_component_static!(AlarmHw));
455
456    // Setup the console.
457    let console = components::console::ConsoleComponent::new(
458        board_kernel,
459        capsules_core::console::DRIVER_NUM,
460        uart_mux,
461    )
462    .finalize(components::console_component_static!());
463
464    // Create the debugger object that handles calls to `debug!()`.
465    components::debug_writer::DebugWriterComponent::new_unsafe(
466        uart_mux,
467        create_capability!(capabilities::SetDebugWriterCapability),
468        || unsafe {
469            kernel::debug::initialize_debug_writer_wrapper_unsafe::<
470                <ChipHw as kernel::platform::chip::Chip>::ThreadIdProvider,
471            >();
472        },
473    )
474    .finalize(components::debug_writer_component_static!());
475
476    let lldb = components::lldb::LowLevelDebugComponent::new(
477        board_kernel,
478        capsules_core::low_level_debug::DRIVER_NUM,
479        uart_mux,
480    )
481    .finalize(components::low_level_debug_component_static!());
482
483    let scheduler = components::sched::mlfq::MLFQComponent::new(mux_alarm, processes).finalize(
484        components::mlfq_component_static!(
485            litex_vexriscv::timer::LiteXAlarm<
486                'static,
487                'static,
488                socc::SoCRegisterFmt,
489                socc::ClockFrequency,
490            >,
491            NUM_PROCS
492        ),
493    );
494
495    let litex_arty = LiteXArty {
496        console,
497        pconsole,
498        alarm,
499        lldb,
500        led_driver,
501        scheduler,
502        scheduler_timer,
503        ipc: kernel::ipc::IPC::new(
504            board_kernel,
505            kernel::ipc::DRIVER_NUM,
506            &memory_allocation_cap,
507        ),
508    };
509
510    debug!("LiteX+VexRiscv on ArtyA7: initialization complete, entering main loop.");
511    let _ = litex_arty.pconsole.start();
512
513    kernel::process::load_processes(
514        board_kernel,
515        chip,
516        core::slice::from_raw_parts(
517            core::ptr::addr_of!(_sapps),
518            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
519        ),
520        core::slice::from_raw_parts_mut(
521            core::ptr::addr_of_mut!(_sappmem),
522            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
523        ),
524        &FAULT_RESPONSE,
525        &process_mgmt_cap,
526    )
527    .unwrap_or_else(|err| {
528        debug!("Error loading processes!");
529        debug!("{:?}", err);
530    });
531
532    (board_kernel, litex_arty, chip)
533}
534
535/// Main function called after RAM initialized.
536#[no_mangle]
537pub unsafe fn main() {
538    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
539
540    let (board_kernel, board, chip) = start();
541    board_kernel.kernel_loop(&board, chip, Some(&board.ipc), &main_loop_capability);
542}