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<
55        'static,
56        { socc::ETHMAC_TX_SLOTS },
57        socc::SoCRegisterFmt,
58    >,
59}
60
61impl LiteXArtyInterruptablePeripherals {
62    // Resolve any recursive dependencies and set up deferred calls:
63    pub fn init(&'static self) {
64        kernel::deferred_call::DeferredCallClient::register(self.uart0);
65    }
66}
67
68impl InterruptService for LiteXArtyInterruptablePeripherals {
69    unsafe fn service_interrupt(&self, interrupt: u32) -> bool {
70        match interrupt as usize {
71            socc::UART_INTERRUPT => {
72                self.uart0.service_interrupt();
73                true
74            }
75            socc::TIMER0_INTERRUPT => {
76                self.timer0.service_interrupt();
77                true
78            }
79            socc::ETHMAC_INTERRUPT => {
80                self.ethmac0.service_interrupt();
81                true
82            }
83            _ => false,
84        }
85    }
86}
87
88const NUM_PROCS: usize = 4;
89
90// Actual memory for holding the active process structures. Need an
91// empty list at least.
92static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
93    [None; NUM_PROCS];
94
95// Reference to the chip, led controller, UART hardware, and process printer for
96// panic dumps.
97struct LiteXArtyPanicReferences {
98    chip: Option<&'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>,
99    uart: Option<&'static litex_vexriscv::uart::LiteXUart<'static, socc::SoCRegisterFmt>>,
100    led_controller:
101        Option<&'static litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>>,
102    process_printer: Option<&'static capsules_system::process_printer::ProcessPrinterText>,
103}
104static mut PANIC_REFERENCES: LiteXArtyPanicReferences = LiteXArtyPanicReferences {
105    chip: None,
106    uart: None,
107    led_controller: None,
108    process_printer: None,
109};
110
111// How should the kernel respond when a process faults.
112const FAULT_RESPONSE: capsules_system::process_policies::PanicFaultPolicy =
113    capsules_system::process_policies::PanicFaultPolicy {};
114
115/// Dummy buffer that causes the linker to reserve enough space for the stack.
116#[no_mangle]
117#[link_section = ".stack_buffer"]
118pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000];
119
120/// A structure representing this platform that holds references to all
121/// capsules for this platform.
122struct LiteXArty {
123    led_driver: &'static capsules_core::led::LedDriver<
124        'static,
125        litex_vexriscv::led_controller::LiteXLed<'static, socc::SoCRegisterFmt>,
126        4,
127    >,
128    console: &'static capsules_core::console::Console<'static>,
129    pconsole: &'static capsules_core::process_console::ProcessConsole<
130        'static,
131        { capsules_core::process_console::DEFAULT_COMMAND_HISTORY_LEN },
132        VirtualMuxAlarm<
133            'static,
134            litex_vexriscv::timer::LiteXAlarm<
135                'static,
136                'static,
137                socc::SoCRegisterFmt,
138                socc::ClockFrequency,
139            >,
140        >,
141        components::process_console::Capability,
142    >,
143    lldb: &'static capsules_core::low_level_debug::LowLevelDebug<
144        'static,
145        capsules_core::virtualizers::virtual_uart::UartDevice<'static>,
146    >,
147    alarm: &'static capsules_core::alarm::AlarmDriver<
148        'static,
149        VirtualMuxAlarm<
150            'static,
151            litex_vexriscv::timer::LiteXAlarm<
152                'static,
153                'static,
154                socc::SoCRegisterFmt,
155                socc::ClockFrequency,
156            >,
157        >,
158    >,
159    ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
160    scheduler: &'static MLFQSched<
161        'static,
162        VirtualMuxAlarm<
163            'static,
164            litex_vexriscv::timer::LiteXAlarm<
165                'static,
166                'static,
167                socc::SoCRegisterFmt,
168                socc::ClockFrequency,
169            >,
170        >,
171    >,
172    scheduler_timer: &'static VirtualSchedulerTimer<
173        VirtualMuxAlarm<
174            'static,
175            litex_vexriscv::timer::LiteXAlarm<
176                'static,
177                'static,
178                socc::SoCRegisterFmt,
179                socc::ClockFrequency,
180            >,
181        >,
182    >,
183}
184
185/// Mapping of integer syscalls to objects that implement syscalls
186impl SyscallDriverLookup for LiteXArty {
187    fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
188    where
189        F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
190    {
191        match driver_num {
192            capsules_core::led::DRIVER_NUM => f(Some(self.led_driver)),
193            capsules_core::console::DRIVER_NUM => f(Some(self.console)),
194            capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
195            capsules_core::low_level_debug::DRIVER_NUM => f(Some(self.lldb)),
196            kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
197            _ => f(None),
198        }
199    }
200}
201
202impl KernelResources<litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>>
203    for LiteXArty
204{
205    type SyscallDriverLookup = Self;
206    type SyscallFilter = ();
207    type ProcessFault = ();
208    type Scheduler = MLFQSched<
209        'static,
210        VirtualMuxAlarm<
211            'static,
212            litex_vexriscv::timer::LiteXAlarm<
213                'static,
214                'static,
215                socc::SoCRegisterFmt,
216                socc::ClockFrequency,
217            >,
218        >,
219    >;
220    type SchedulerTimer = VirtualSchedulerTimer<
221        VirtualMuxAlarm<
222            'static,
223            litex_vexriscv::timer::LiteXAlarm<
224                'static,
225                'static,
226                socc::SoCRegisterFmt,
227                socc::ClockFrequency,
228            >,
229        >,
230    >;
231    type WatchDog = ();
232    type ContextSwitchCallback = ();
233
234    fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
235        self
236    }
237    fn syscall_filter(&self) -> &Self::SyscallFilter {
238        &()
239    }
240    fn process_fault(&self) -> &Self::ProcessFault {
241        &()
242    }
243    fn scheduler(&self) -> &Self::Scheduler {
244        self.scheduler
245    }
246    fn scheduler_timer(&self) -> &Self::SchedulerTimer {
247        self.scheduler_timer
248    }
249    fn watchdog(&self) -> &Self::WatchDog {
250        &()
251    }
252    fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
253        &()
254    }
255}
256
257/// This is in a separate, inline(never) function so that its stack frame is
258/// removed when this function returns. Otherwise, the stack space used for
259/// these static_inits is wasted.
260#[inline(never)]
261unsafe fn start() -> (
262    &'static kernel::Kernel,
263    LiteXArty,
264    &'static litex_vexriscv::chip::LiteXVexRiscv<LiteXArtyInterruptablePeripherals>,
265) {
266    // These symbols are defined in the linker script.
267    extern "C" {
268        /// Beginning of the ROM region containing app images.
269        static _sapps: u8;
270        /// End of the ROM region containing app images.
271        static _eapps: u8;
272        /// Beginning of the RAM region for app memory.
273        static mut _sappmem: u8;
274        /// End of the RAM region for app memory.
275        static _eappmem: u8;
276        /// The start of the kernel text (Included only for kernel PMP)
277        static _stext: u8;
278        /// The end of the kernel text (Included only for kernel PMP)
279        static _etext: u8;
280        /// The start of the kernel / app / storage flash (Included only for kernel PMP)
281        static _sflash: u8;
282        /// The end of the kernel / app / storage flash (Included only for kernel PMP)
283        static _eflash: u8;
284        /// The start of the kernel / app RAM (Included only for kernel PMP)
285        static _ssram: u8;
286        /// The end of the kernel / app RAM (Included only for kernel PMP)
287        static _esram: u8;
288    }
289
290    // ---------- BASIC INITIALIZATION ----------
291
292    // Basic setup of the riscv platform.
293    rv32i::configure_trap_handler();
294
295    // Set up memory protection immediately after setting the trap handler, to
296    // ensure that much of the board initialization routine runs with PMP kernel
297    // memory protection.
298    let pmp = rv32i::pmp::kernel_protection::KernelProtectionPMP::new(
299        rv32i::pmp::kernel_protection::FlashRegion(
300            rv32i::pmp::NAPOTRegionSpec::from_start_end(
301                core::ptr::addr_of!(_sflash),
302                core::ptr::addr_of!(_eflash),
303            )
304            .unwrap(),
305        ),
306        rv32i::pmp::kernel_protection::RAMRegion(
307            rv32i::pmp::NAPOTRegionSpec::from_start_end(
308                core::ptr::addr_of!(_ssram),
309                core::ptr::addr_of!(_esram),
310            )
311            .unwrap(),
312        ),
313        rv32i::pmp::kernel_protection::MMIORegion(
314            rv32i::pmp::NAPOTRegionSpec::from_start_size(
315                0xf0000000 as *const u8, // start
316                0x10000000,              // size
317            )
318            .unwrap(),
319        ),
320        rv32i::pmp::kernel_protection::KernelTextRegion(
321            rv32i::pmp::TORRegionSpec::from_start_end(
322                core::ptr::addr_of!(_stext),
323                core::ptr::addr_of!(_etext),
324            )
325            .unwrap(),
326        ),
327    )
328    .unwrap();
329
330    // initialize capabilities
331    let process_mgmt_cap = create_capability!(capabilities::ProcessManagementCapability);
332    let memory_allocation_cap = create_capability!(capabilities::MemoryAllocationCapability);
333
334    let board_kernel = static_init!(kernel::Kernel, kernel::Kernel::new(&*addr_of!(PROCESSES)));
335
336    // ---------- LED CONTROLLER HARDWARE ----------
337
338    // Initialize the LEDs, stopping any patterns from the bootloader
339    // / bios still running in HW and turn them all off
340    let led0 = static_init!(
341        litex_vexriscv::led_controller::LiteXLedController<socc::SoCRegisterFmt>,
342        litex_vexriscv::led_controller::LiteXLedController::new(
343            StaticRef::new(
344                socc::CSR_LEDS_BASE
345                    as *const litex_vexriscv::led_controller::LiteXLedRegisters<
346                        socc::SoCRegisterFmt,
347                    >
348            ),
349            4, // 4 LEDs on this board
350        )
351    );
352    led0.initialize();
353
354    PANIC_REFERENCES.led_controller = Some(led0);
355
356    // --------- TIMER & UPTIME CORE; ALARM INITIALIZATION ----------
357
358    // Initialize the hardware timer
359    let timer0 = static_init!(
360        litex_vexriscv::timer::LiteXTimer<'static, socc::SoCRegisterFmt, socc::ClockFrequency>,
361        litex_vexriscv::timer::LiteXTimer::new(StaticRef::new(
362            socc::CSR_TIMER0_BASE
363                as *const litex_vexriscv::timer::LiteXTimerRegisters<socc::SoCRegisterFmt>
364        ),)
365    );
366
367    // The SoC is expected to feature the 64-bit uptime extension to the timer hardware
368    let timer0_uptime = static_init!(
369        litex_vexriscv::timer::LiteXTimerUptime<
370            'static,
371            socc::SoCRegisterFmt,
372            socc::ClockFrequency,
373        >,
374        litex_vexriscv::timer::LiteXTimerUptime::new(timer0)
375    );
376
377    // Create the LiteXAlarm based on the hardware LiteXTimer core and
378    // the uptime peripheral
379    let litex_alarm = static_init!(
380        litex_vexriscv::timer::LiteXAlarm<
381            'static,
382            'static,
383            socc::SoCRegisterFmt,
384            socc::ClockFrequency,
385        >,
386        litex_vexriscv::timer::LiteXAlarm::new(timer0_uptime, timer0)
387    );
388    timer0.set_timer_client(litex_alarm);
389    litex_alarm.initialize();
390
391    // Create a shared virtualization mux layer on top of a single hardware
392    // alarm.
393    let mux_alarm = static_init!(
394        MuxAlarm<
395            'static,
396            litex_vexriscv::timer::LiteXAlarm<
397                'static,
398                'static,
399                socc::SoCRegisterFmt,
400                socc::ClockFrequency,
401            >,
402        >,
403        MuxAlarm::new(litex_alarm)
404    );
405    litex_alarm.set_alarm_client(mux_alarm);
406
407    // Userspace alarm driver
408    let virtual_alarm_user = static_init!(
409        VirtualMuxAlarm<
410            'static,
411            litex_vexriscv::timer::LiteXAlarm<
412                'static,
413                'static,
414                socc::SoCRegisterFmt,
415                socc::ClockFrequency,
416            >,
417        >,
418        VirtualMuxAlarm::new(mux_alarm)
419    );
420    virtual_alarm_user.setup();
421
422    let alarm = static_init!(
423        capsules_core::alarm::AlarmDriver<
424            'static,
425            VirtualMuxAlarm<
426                'static,
427                litex_vexriscv::timer::LiteXAlarm<
428                    'static,
429                    'static,
430                    socc::SoCRegisterFmt,
431                    socc::ClockFrequency,
432                >,
433            >,
434        >,
435        capsules_core::alarm::AlarmDriver::new(
436            virtual_alarm_user,
437            board_kernel.create_grant(capsules_core::alarm::DRIVER_NUM, &memory_allocation_cap)
438        )
439    );
440    virtual_alarm_user.set_alarm_client(alarm);
441
442    // Systick virtual alarm for scheduling
443    let systick_virtual_alarm = static_init!(
444        VirtualMuxAlarm<
445            'static,
446            litex_vexriscv::timer::LiteXAlarm<
447                'static,
448                'static,
449                socc::SoCRegisterFmt,
450                socc::ClockFrequency,
451            >,
452        >,
453        VirtualMuxAlarm::new(mux_alarm)
454    );
455    systick_virtual_alarm.setup();
456
457    let scheduler_timer = static_init!(
458        VirtualSchedulerTimer<
459            VirtualMuxAlarm<
460                'static,
461                litex_vexriscv::timer::LiteXAlarm<
462                    'static,
463                    'static,
464                    socc::SoCRegisterFmt,
465                    socc::ClockFrequency,
466                >,
467            >,
468        >,
469        VirtualSchedulerTimer::new(systick_virtual_alarm)
470    );
471
472    // ---------- UART ----------
473
474    // Initialize the HW UART
475    let uart0 = static_init!(
476        litex_vexriscv::uart::LiteXUart<socc::SoCRegisterFmt>,
477        litex_vexriscv::uart::LiteXUart::new(
478            StaticRef::new(
479                socc::CSR_UART_BASE
480                    as *const litex_vexriscv::uart::LiteXUartRegisters<socc::SoCRegisterFmt>,
481            ),
482            // No UART PHY CSR present, thus baudrate fixed in
483            // hardware. Change with --uart-baudrate during SoC
484            // generation. Fixed to 1MBd.
485            None,
486        )
487    );
488    uart0.initialize();
489
490    PANIC_REFERENCES.uart = Some(uart0);
491
492    // Create a shared UART channel for the console and for kernel debug.
493    let uart_mux = components::console::UartMuxComponent::new(uart0, socc::UART_BAUDRATE)
494        .finalize(components::uart_mux_component_static!());
495
496    // ---------- ETHERNET ----------
497
498    // ETHMAC peripheral
499    let ethmac0 = static_init!(
500        litex_vexriscv::liteeth::LiteEth<{socc::ETHMAC_TX_SLOTS}, socc::SoCRegisterFmt>,
501        litex_vexriscv::liteeth::LiteEth::new(
502            StaticRef::new(
503                socc::CSR_ETHMAC_BASE
504                    as *const litex_vexriscv::liteeth::LiteEthMacRegisters<socc::SoCRegisterFmt>,
505            ),
506            socc::MEM_ETHMAC_BASE,
507            socc::MEM_ETHMAC_SIZE,
508            socc::ETHMAC_SLOT_SIZE,
509            socc::ETHMAC_RX_SLOTS,
510            socc::ETHMAC_TX_SLOTS,
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}