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// Disable this attribute when documenting, as a workaround for
10// https://github.com/rust-lang/rust/issues/62184.
11#![cfg_attr(not(doc), no_main)]
12
13use core::ptr::{addr_of, addr_of_mut};
14
15use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
16
17use kernel::capabilities;
18use kernel::component::Component;
19use kernel::hil::time::{Alarm, Timer};
20use kernel::platform::chip::InterruptService;
21use kernel::platform::scheduler_timer::VirtualSchedulerTimer;
22use kernel::platform::{KernelResources, SyscallDriverLookup};
23use kernel::scheduler::mlfq::MLFQSched;
24use kernel::utilities::registers::interfaces::ReadWriteable;
25use kernel::utilities::StaticRef;
26use kernel::{create_capability, debug, static_init};
27use rv32i::csr;
28
29mod io;
30mod litex_generated_constants;
31
32// This module contains the LiteX SoC configuration options, register
33// positions, interrupt mappings and other implementation details of
34// the generated bitstream.
35//
36// Its values are used throughout the file, hence import it under a
37// short name.
38use litex_generated_constants as socc;
39
40/// Structure for dynamic interrupt mapping, depending on the SoC
41/// configuration
42///
43/// This struct is deliberately kept in the board crate. Because of
44/// the configurable nature of LiteX, it does not make sense to define
45/// a default interrupt mapping, as the interrupt numbers are
46/// generated sequentially for all softcores.
47struct LiteXArtyInterruptablePeripherals {
48    uart0: &'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>,
49    timer0: &'static litex_vexriscv::timer::LiteXTimer<
50        'static,
51        socc::SoCRegisterFmt,
52        socc::ClockFrequency,
53    >,
54    ethmac0: &'static litex_vexriscv::liteeth::LiteEth<'static, socc::SoCRegisterFmt>,
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
86// Actual memory for holding the active process structures. Need an
87// empty list at least.
88static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
89    [None; NUM_PROCS];
90
91// Reference to the chip, led controller, UART hardware, and process printer for
92// panic dumps.
93struct LiteXArtyPanicReferences {
94    chip: Option<&'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>,
95    uart: Option<&'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>>,
96    led_controller:
97        Option<&'static litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>>,
98    process_printer: Option<&'static capsules_system::process_printer::ProcessPrinterText>,
99}
100static mut PANIC_REFERENCES: LiteXArtyPanicReferences = LiteXArtyPanicReferences {
101    chip: None,
102    uart: None,
103    led_controller: None,
104    process_printer: None,
105};
106
107// How should the kernel respond when a process faults.
108const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
109    capsules_system::process_policies::PanicFaultPolicy {};
110
111/// Dummy buffer that causes the linker to reserve enough space for the stack.
112#[no_mangle]
113#[link_section = ".stack_buffer"]
114pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000];
115
116/// A structure representing this platform that holds references to all
117/// capsules for this platform.
118struct LiteXArty {
119    led_driver: &'static capsules_core::led::LedDriver<
120        'static,
121        litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
122        4,
123    >,
124    console: &'static capsules_core::console::Console<'static>,
125    pconsole: &'static capsules_core::process_console::ProcessConsole<
126        'static,
127        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
128        VirtualMuxAlarm<
129            'static,
130            litex_vexriscv::timer::LiteXAlarm<
131                'static,
132                'static,
133                socc::SoCRegisterFmt,
134                socc::ClockFrequency,
135            >,
136        >,
137        components::process_console::Capability,
138    >,
139    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
140        'static,
141        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
142    >,
143    alarm: &'static capsules_core::alarm::AlarmDriver<
144        'static,
145        VirtualMuxAlarm<
146            'static,
147            litex_vexriscv::timer::LiteXAlarm<
148                'static,
149                'static,
150                socc::SoCRegisterFmt,
151                socc::ClockFrequency,
152            >,
153        >,
154    >,
155    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
156    scheduler: &'static MLFQSched<
157        'static,
158        VirtualMuxAlarm<
159            'static,
160            litex_vexriscv::timer::LiteXAlarm<
161                'static,
162                'static,
163                socc::SoCRegisterFmt,
164                socc::ClockFrequency,
165            >,
166        >,
167    >,
168    scheduler_timer: &'static VirtualSchedulerTimer<
169        VirtualMuxAlarm<
170            'static,
171            litex_vexriscv::timer::LiteXAlarm<
172                'static,
173                'static,
174                socc::SoCRegisterFmt,
175                socc::ClockFrequency,
176            >,
177        >,
178    >,
179}
180
181/// Mapping of integer syscalls to objects that implement syscalls
182impl SyscallDriverLookup for LiteXArty {
183    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
184    where
185        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
186    {
187        match driver_num {
188            capsules_core::led::DRIVER_NUM => f(Some(self.led_driver)),
189            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
190            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
191            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
192            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
193            _ => f(None),
194        }
195    }
196}
197
198impl KernelResources<litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>
199    for LiteXArty
200{
201    type SyscallDriverLookup = Self;
202    type SyscallFilter = ();
203    type ProcessFault = ();
204    type Scheduler = MLFQSched<
205        'static,
206        VirtualMuxAlarm<
207            'static,
208            litex_vexriscv::timer::LiteXAlarm<
209                'static,
210                'static,
211                socc::SoCRegisterFmt,
212                socc::ClockFrequency,
213            >,
214        >,
215    >;
216    type SchedulerTimer = VirtualSchedulerTimer<
217        VirtualMuxAlarm<
218            'static,
219            litex_vexriscv::timer::LiteXAlarm<
220                'static,
221                'static,
222                socc::SoCRegisterFmt,
223                socc::ClockFrequency,
224            >,
225        >,
226    >;
227    type WatchDog = ();
228    type ContextSwitchCallback = ();
229
230    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
231        self
232    }
233    fn syscall_filter(&self) -> &Self::SyscallFilter {
234        &()
235    }
236    fn process_fault(&self) -> &Self::ProcessFault {
237        &()
238    }
239    fn scheduler(&self) -> &Self::Scheduler {
240        self.scheduler
241    }
242    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
243        self.scheduler_timer
244    }
245    fn watchdog(&self) -> &Self::WatchDog {
246        &()
247    }
248    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
249        &()
250    }
251}
252
253/// This is in a separate, inline(never) function so that its stack frame is
254/// removed when this function returns. Otherwise, the stack space used for
255/// these static_inits is wasted.
256#[inline(never)]
257unsafe fn start() -> (
258    &'static kernel::Kernel,
259    LiteXArty,
260    &'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>,
261) {
262    // These symbols are defined in the linker script.
263    extern "C" {
264        /// Beginning of the ROM region containing app images.
265        static _sapps: u8;
266        /// End of the ROM region containing app images.
267        static _eapps: u8;
268        /// Beginning of the RAM region for app memory.
269        static mut _sappmem: u8;
270        /// End of the RAM region for app memory.
271        static _eappmem: u8;
272        /// The start of the kernel text (Included only for kernel PMP)
273        static _stext: u8;
274        /// The end of the kernel text (Included only for kernel PMP)
275        static _etext: u8;
276        /// The start of the kernel / app / storage flash (Included only for kernel PMP)
277        static _sflash: u8;
278        /// The end of the kernel / app / storage flash (Included only for kernel PMP)
279        static _eflash: u8;
280        /// The start of the kernel / app RAM (Included only for kernel PMP)
281        static _ssram: u8;
282        /// The end of the kernel / app RAM (Included only for kernel PMP)
283        static _esram: u8;
284    }
285
286    // ---------- BASIC INITIALIZATION ----------
287
288    // Basic setup of the riscv platform.
289    rv32i::configure_trap_handler();
290
291    // Set up memory protection immediately after setting the trap handler, to
292    // ensure that much of the board initialization routine runs with PMP kernel
293    // memory protection.
294    let pmp = rv32i::pmp::kernel_protection::KernelProtectionPMP::new(
295        rv32i::pmp::kernel_protection::FlashRegion(
296            rv32i::pmp::NAPOTRegionSpec::new(
297                core::ptr::addr_of!(_sflash),
298                core::ptr::addr_of!(_eflash) as usize - core::ptr::addr_of!(_sflash) as usize,
299            )
300            .unwrap(),
301        ),
302        rv32i::pmp::kernel_protection::RAMRegion(
303            rv32i::pmp::NAPOTRegionSpec::new(
304                core::ptr::addr_of!(_ssram),
305                core::ptr::addr_of!(_esram) as usize - core::ptr::addr_of!(_ssram) as usize,
306            )
307            .unwrap(),
308        ),
309        rv32i::pmp::kernel_protection::MMIORegion(
310            rv32i::pmp::NAPOTRegionSpec::new(
311                0xf0000000 as *const u8, // start
312                0x10000000,              // size
313            )
314            .unwrap(),
315        ),
316        rv32i::pmp::kernel_protection::KernelTextRegion(
317            rv32i::pmp::TORRegionSpec::new(
318                core::ptr::addr_of!(_stext),
319                core::ptr::addr_of!(_etext),
320            )
321            .unwrap(),
322        ),
323    )
324    .unwrap();
325
326    // initialize capabilities
327    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
328    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
329
330    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
331
332    // ---------- LED CONTROLLER HARDWARE ----------
333
334    // Initialize the LEDs, stopping any patterns from the bootloader
335    // / bios still running in HW and turn them all off
336    let led0 = static_init!(
337        litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>,
338        litex_vexriscv::led_controller::LiteXLedController::new(
339            StaticRef::new(
340                socc::CSR_LEDS_BASE
341                    as *const litex_vexriscv::led_controller::LiteXLedRegisters<
342                        socc::SoCRegisterFmt,
343                    >
344            ),
345            4, // 4 LEDs on this board
346        )
347    );
348    led0.initialize();
349
350    PANIC_REFERENCES.led_controller = Some(led0);
351
352    // --------- TIMER & UPTIME CORE; ALARM INITIALIZATION ----------
353
354    // Initialize the hardware timer
355    let timer0 = static_init!(
356        litex_vexriscv::timer::LiteXTimer<'static, socc::SoCRegisterFmt, socc::ClockFrequency>,
357        litex_vexriscv::timer::LiteXTimer::new(StaticRef::new(
358            socc::CSR_TIMER0_BASE
359                as *const litex_vexriscv::timer::LiteXTimerRegisters<socc::SoCRegisterFmt>
360        ),)
361    );
362
363    // The SoC is expected to feature the 64-bit uptime extension to the timer hardware
364    let timer0_uptime = static_init!(
365        litex_vexriscv::timer::LiteXTimerUptime<
366            'static,
367            socc::SoCRegisterFmt,
368            socc::ClockFrequency,
369        >,
370        litex_vexriscv::timer::LiteXTimerUptime::new(timer0)
371    );
372
373    // Create the LiteXAlarm based on the hardware LiteXTimer core and
374    // the uptime peripheral
375    let litex_alarm = static_init!(
376        litex_vexriscv::timer::LiteXAlarm<
377            'static,
378            'static,
379            socc::SoCRegisterFmt,
380            socc::ClockFrequency,
381        >,
382        litex_vexriscv::timer::LiteXAlarm::new(timer0_uptime, timer0)
383    );
384    timer0.set_timer_client(litex_alarm);
385    litex_alarm.initialize();
386
387    // Create a shared virtualization mux layer on top of a single hardware
388    // alarm.
389    let mux_alarm = static_init!(
390        MuxAlarm<
391            'static,
392            litex_vexriscv::timer::LiteXAlarm<
393                'static,
394                'static,
395                socc::SoCRegisterFmt,
396                socc::ClockFrequency,
397            >,
398        >,
399        MuxAlarm::new(litex_alarm)
400    );
401    litex_alarm.set_alarm_client(mux_alarm);
402
403    // Userspace alarm driver
404    let virtual_alarm_user = static_init!(
405        VirtualMuxAlarm<
406            'static,
407            litex_vexriscv::timer::LiteXAlarm<
408                'static,
409                'static,
410                socc::SoCRegisterFmt,
411                socc::ClockFrequency,
412            >,
413        >,
414        VirtualMuxAlarm::new(mux_alarm)
415    );
416    virtual_alarm_user.setup();
417
418    let alarm = static_init!(
419        capsules_core::alarm::AlarmDriver<
420            'static,
421            VirtualMuxAlarm<
422                'static,
423                litex_vexriscv::timer::LiteXAlarm<
424                    'static,
425                    'static,
426                    socc::SoCRegisterFmt,
427                    socc::ClockFrequency,
428                >,
429            >,
430        >,
431        capsules_core::alarm::AlarmDriver::new(
432            virtual_alarm_user,
433            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
434        )
435    );
436    virtual_alarm_user.set_alarm_client(alarm);
437
438    // Systick virtual alarm for scheduling
439    let systick_virtual_alarm = static_init!(
440        VirtualMuxAlarm<
441            'static,
442            litex_vexriscv::timer::LiteXAlarm<
443                'static,
444                'static,
445                socc::SoCRegisterFmt,
446                socc::ClockFrequency,
447            >,
448        >,
449        VirtualMuxAlarm::new(mux_alarm)
450    );
451    systick_virtual_alarm.setup();
452
453    let scheduler_timer = static_init!(
454        VirtualSchedulerTimer<
455            VirtualMuxAlarm<
456                'static,
457                litex_vexriscv::timer::LiteXAlarm<
458                    'static,
459                    'static,
460                    socc::SoCRegisterFmt,
461                    socc::ClockFrequency,
462                >,
463            >,
464        >,
465        VirtualSchedulerTimer::new(systick_virtual_alarm)
466    );
467
468    // ---------- UART ----------
469
470    // Initialize the HW UART
471    let uart0 = static_init!(
472        litex_vexriscv::uart::LiteXUart<socc::SoCRegisterFmt>,
473        litex_vexriscv::uart::LiteXUart::new(
474            StaticRef::new(
475                socc::CSR_UART_BASE
476                    as *const litex_vexriscv::uart::LiteXUartRegisters<socc::SoCRegisterFmt>,
477            ),
478            // No UART PHY CSR present, thus baudrate fixed in
479            // hardware. Change with --uart-baudrate during SoC
480            // generation. Fixed to 1MBd.
481            None,
482        )
483    );
484    uart0.initialize();
485
486    PANIC_REFERENCES.uart = Some(uart0);
487
488    // Create a shared UART channel for the console and for kernel debug.
489    let uart_mux = components::console::UartMuxComponent::new(uart0, socc::UART_BAUDRATE)
490        .finalize(components::uart_mux_component_static!());
491
492    // ---------- ETHERNET ----------
493
494    // Packet receive buffer
495    let ethmac0_rxbuf0 = static_init!([u8; 1522], [0; 1522]);
496
497    // ETHMAC peripheral
498    let ethmac0 = static_init!(
499        litex_vexriscv::liteeth::LiteEth<socc::SoCRegisterFmt>,
500        litex_vexriscv::liteeth::LiteEth::new(
501            StaticRef::new(
502                socc::CSR_ETHMAC_BASE
503                    as *const litex_vexriscv::liteeth::LiteEthMacRegisters<socc::SoCRegisterFmt>,
504            ),
505            socc::MEM_ETHMAC_BASE,
506            socc::MEM_ETHMAC_SIZE,
507            socc::ETHMAC_SLOT_SIZE,
508            socc::ETHMAC_RX_SLOTS,
509            socc::ETHMAC_TX_SLOTS,
510            ethmac0_rxbuf0,
511        )
512    );
513
514    // Initialize the ETHMAC controller
515    ethmac0.initialize();
516
517    // ---------- LED DRIVER ----------
518
519    // LEDs
520    let led_driver =
521        components::led::LedsComponent::new().finalize(components::led_component_static!(
522            litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
523            led0.get_led(0).unwrap(),
524            led0.get_led(1).unwrap(),
525            led0.get_led(2).unwrap(),
526            led0.get_led(3).unwrap(),
527        ));
528
529    // ---------- INITIALIZE CHIP, ENABLE INTERRUPTS ----------
530
531    let interrupt_service = static_init!(
532        LiteXArtyInterruptablePeripherals,
533        LiteXArtyInterruptablePeripherals {
534            uart0,
535            timer0,
536            ethmac0,
537        }
538    );
539    interrupt_service.init();
540
541    let chip = static_init!(
542        litex_vexriscv::chip::LiteXVexRiscv<
543            LiteXArtyInterruptablePeripherals,
544        >,
545        litex_vexriscv::chip::LiteXVexRiscv::new(
546            "LiteX on Arty A7",
547            interrupt_service,
548            pmp,
549        )
550    );
551
552    PANIC_REFERENCES.chip = Some(chip);
553
554    let process_printer = components::process_printer::ProcessPrinterTextComponent::new()
555        .finalize(components::process_printer_text_component_static!());
556
557    PANIC_REFERENCES.process_printer = Some(process_printer);
558
559    // Enable RISC-V interrupts globally
560    csr::CSR
561        .mie
562        .modify(csr::mie::mie::mext::SET + csr::mie::mie::msoft::SET);
563    csr::CSR.mstatus.modify(csr::mstatus::mstatus::mie::SET);
564
565    // Unmask all interrupt sources in the interrupt controller
566    chip.unmask_interrupts();
567
568    // Setup the process console.
569    let pconsole = components::process_console::ProcessConsoleComponent::new(
570        board_kernel,
571        uart_mux,
572        mux_alarm,
573        process_printer,
574        None,
575    )
576    .finalize(components::process_console_component_static!(
577        litex_vexriscv::timer::LiteXAlarm<
578            'static,
579            'static,
580            socc::SoCRegisterFmt,
581            socc::ClockFrequency,
582        >
583    ));
584
585    // Setup the console.
586    let console = components::console::ConsoleComponent::new(
587        board_kernel,
588        capsules_core::console::DRIVER_NUM,
589        uart_mux,
590    )
591    .finalize(components::console_component_static!());
592
593    // Create the debugger object that handles calls to `debug!()`.
594    components::debug_writer::DebugWriterComponent::new(uart_mux)
595        .finalize(components::debug_writer_component_static!());
596
597    let lldb = components::lldb::LowLevelDebugComponent::new(
598        board_kernel,
599        capsules_core::low_level_debug::DRIVER_NUM,
600        uart_mux,
601    )
602    .finalize(components::low_level_debug_component_static!());
603
604    let scheduler = components::sched::mlfq::MLFQComponent::new(mux_alarm, &*addr_of!(PROCESSES))
605        .finalize(components::mlfq_component_static!(
606            litex_vexriscv::timer::LiteXAlarm<
607                'static,
608                'static,
609                socc::SoCRegisterFmt,
610                socc::ClockFrequency,
611            >,
612            NUM_PROCS
613        ));
614
615    let litex_arty = LiteXArty {
616        console,
617        pconsole,
618        alarm,
619        lldb,
620        led_driver,
621        scheduler,
622        scheduler_timer,
623        ipc: kernel::ipc::IPC::new(
624            board_kernel,
625            kernel::ipc::DRIVER_NUM,
626            &memory_allocation_cap,
627        ),
628    };
629
630    debug!("LiteX+VexRiscv on ArtyA7: initialization complete, entering main loop.");
631    let _ = litex_arty.pconsole.start();
632
633    kernel::process::load_processes(
634        board_kernel,
635        chip,
636        core::slice::from_raw_parts(
637            core::ptr::addr_of!(_sapps),
638            core::ptr::addr_of!(_eapps) as usize - core::ptr::addr_of!(_sapps) as usize,
639        ),
640        core::slice::from_raw_parts_mut(
641            core::ptr::addr_of_mut!(_sappmem),
642            core::ptr::addr_of!(_eappmem) as usize - core::ptr::addr_of!(_sappmem) as usize,
643        ),
644        &mut *addr_of_mut!(PROCESSES),
645        &FAULT_RESPONSE,
646        &process_mgmt_cap,
647    )
648    .unwrap_or_else(|err| {
649        debug!("Error loading processes!");
650        debug!("{:?}", err);
651    });
652
653    (board_kernel, litex_arty, chip)
654}
655
656/// Main function called after RAM initialized.
657#[no_mangle]
658pub unsafe fn main() {
659    let main_loop_capability = create_capability!(capabilities::MainLoopCapability);
660
661    let (board_kernel, board, chip) = start();
662    board_kernel.kernel_loop(&board, chip, Some(&board.ipc), &main_loop_capability);
663}